xref: /openbmc/linux/drivers/scsi/ibmvscsi/ibmvfc.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2072b91f9SBrian King /*
3072b91f9SBrian King  * ibmvfc.c -- driver for IBM Power Virtual Fibre Channel Adapter
4072b91f9SBrian King  *
5072b91f9SBrian King  * Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
6072b91f9SBrian King  *
7072b91f9SBrian King  * Copyright (C) IBM Corporation, 2008
8072b91f9SBrian King  */
9072b91f9SBrian King 
10072b91f9SBrian King #include <linux/module.h>
11072b91f9SBrian King #include <linux/moduleparam.h>
12072b91f9SBrian King #include <linux/dma-mapping.h>
13072b91f9SBrian King #include <linux/dmapool.h>
14072b91f9SBrian King #include <linux/delay.h>
15072b91f9SBrian King #include <linux/interrupt.h>
165951be4cSMarc Zyngier #include <linux/irqdomain.h>
17072b91f9SBrian King #include <linux/kthread.h>
185a0e3ad6STejun Heo #include <linux/slab.h>
19072b91f9SBrian King #include <linux/of.h>
20b0f4d4cfSBrian King #include <linux/pm.h>
21072b91f9SBrian King #include <linux/stringify.h>
2275cc8cfcSJohannes Thumshirn #include <linux/bsg-lib.h>
23072b91f9SBrian King #include <asm/firmware.h>
24072b91f9SBrian King #include <asm/irq.h>
25072b91f9SBrian King #include <asm/vio.h>
26072b91f9SBrian King #include <scsi/scsi.h>
27072b91f9SBrian King #include <scsi/scsi_cmnd.h>
28072b91f9SBrian King #include <scsi/scsi_host.h>
29072b91f9SBrian King #include <scsi/scsi_device.h>
30072b91f9SBrian King #include <scsi/scsi_tcq.h>
31072b91f9SBrian King #include <scsi/scsi_transport_fc.h>
32d31429e1SBrian King #include <scsi/scsi_bsg_fc.h>
33072b91f9SBrian King #include "ibmvfc.h"
34072b91f9SBrian King 
35072b91f9SBrian King static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT;
36072b91f9SBrian King static unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT;
371abf635dSHannes Reinecke static u64 max_lun = IBMVFC_MAX_LUN;
38072b91f9SBrian King static unsigned int max_targets = IBMVFC_MAX_TARGETS;
39072b91f9SBrian King static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT;
40072b91f9SBrian King static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS;
41072b91f9SBrian King static unsigned int ibmvfc_debug = IBMVFC_DEBUG;
42072b91f9SBrian King static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL;
43a6104b1eSTyrel Datwyler static unsigned int cls3_error = IBMVFC_CLS3_ERROR;
44032d1900STyrel Datwyler static unsigned int mq_enabled = IBMVFC_MQ;
45032d1900STyrel Datwyler static unsigned int nr_scsi_hw_queues = IBMVFC_SCSI_HW_QUEUES;
46032d1900STyrel Datwyler static unsigned int nr_scsi_channels = IBMVFC_SCSI_CHANNELS;
47032d1900STyrel Datwyler static unsigned int mig_channels_only = IBMVFC_MIG_NO_SUB_TO_CRQ;
48032d1900STyrel Datwyler static unsigned int mig_no_less_channels = IBMVFC_MIG_NO_N_TO_M;
49032d1900STyrel Datwyler 
50072b91f9SBrian King static LIST_HEAD(ibmvfc_head);
51072b91f9SBrian King static DEFINE_SPINLOCK(ibmvfc_driver_lock);
52072b91f9SBrian King static struct scsi_transport_template *ibmvfc_transport_template;
53072b91f9SBrian King 
54072b91f9SBrian King MODULE_DESCRIPTION("IBM Virtual Fibre Channel Driver");
55072b91f9SBrian King MODULE_AUTHOR("Brian King <brking@linux.vnet.ibm.com>");
56072b91f9SBrian King MODULE_LICENSE("GPL");
57072b91f9SBrian King MODULE_VERSION(IBMVFC_DRIVER_VERSION);
58072b91f9SBrian King 
59032d1900STyrel Datwyler module_param_named(mq, mq_enabled, uint, S_IRUGO);
60032d1900STyrel Datwyler MODULE_PARM_DESC(mq, "Enable multiqueue support. "
61032d1900STyrel Datwyler 		 "[Default=" __stringify(IBMVFC_MQ) "]");
62032d1900STyrel Datwyler module_param_named(scsi_host_queues, nr_scsi_hw_queues, uint, S_IRUGO);
63032d1900STyrel Datwyler MODULE_PARM_DESC(scsi_host_queues, "Number of SCSI Host submission queues. "
64032d1900STyrel Datwyler 		 "[Default=" __stringify(IBMVFC_SCSI_HW_QUEUES) "]");
65032d1900STyrel Datwyler module_param_named(scsi_hw_channels, nr_scsi_channels, uint, S_IRUGO);
66032d1900STyrel Datwyler MODULE_PARM_DESC(scsi_hw_channels, "Number of hw scsi channels to request. "
67032d1900STyrel Datwyler 		 "[Default=" __stringify(IBMVFC_SCSI_CHANNELS) "]");
68032d1900STyrel Datwyler module_param_named(mig_channels_only, mig_channels_only, uint, S_IRUGO);
69032d1900STyrel Datwyler MODULE_PARM_DESC(mig_channels_only, "Prevent migration to non-channelized system. "
70032d1900STyrel Datwyler 		 "[Default=" __stringify(IBMVFC_MIG_NO_SUB_TO_CRQ) "]");
71032d1900STyrel Datwyler module_param_named(mig_no_less_channels, mig_no_less_channels, uint, S_IRUGO);
72032d1900STyrel Datwyler MODULE_PARM_DESC(mig_no_less_channels, "Prevent migration to system with less channels. "
73032d1900STyrel Datwyler 		 "[Default=" __stringify(IBMVFC_MIG_NO_N_TO_M) "]");
74032d1900STyrel Datwyler 
75072b91f9SBrian King module_param_named(init_timeout, init_timeout, uint, S_IRUGO | S_IWUSR);
76072b91f9SBrian King MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds. "
77072b91f9SBrian King 		 "[Default=" __stringify(IBMVFC_INIT_TIMEOUT) "]");
78072b91f9SBrian King module_param_named(default_timeout, default_timeout, uint, S_IRUGO | S_IWUSR);
79072b91f9SBrian King MODULE_PARM_DESC(default_timeout,
80072b91f9SBrian King 		 "Default timeout in seconds for initialization and EH commands. "
81072b91f9SBrian King 		 "[Default=" __stringify(IBMVFC_DEFAULT_TIMEOUT) "]");
82072b91f9SBrian King module_param_named(max_requests, max_requests, uint, S_IRUGO);
83072b91f9SBrian King MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter. "
84072b91f9SBrian King 		 "[Default=" __stringify(IBMVFC_MAX_REQUESTS_DEFAULT) "]");
851abf635dSHannes Reinecke module_param_named(max_lun, max_lun, ullong, S_IRUGO);
86072b91f9SBrian King MODULE_PARM_DESC(max_lun, "Maximum allowed LUN. "
87072b91f9SBrian King 		 "[Default=" __stringify(IBMVFC_MAX_LUN) "]");
88072b91f9SBrian King module_param_named(max_targets, max_targets, uint, S_IRUGO);
89072b91f9SBrian King MODULE_PARM_DESC(max_targets, "Maximum allowed targets. "
90072b91f9SBrian King 		 "[Default=" __stringify(IBMVFC_MAX_TARGETS) "]");
91545ef9a2SBrian King module_param_named(disc_threads, disc_threads, uint, S_IRUGO);
92072b91f9SBrian King MODULE_PARM_DESC(disc_threads, "Number of device discovery threads to use. "
93072b91f9SBrian King 		 "[Default=" __stringify(IBMVFC_MAX_DISC_THREADS) "]");
94072b91f9SBrian King module_param_named(debug, ibmvfc_debug, uint, S_IRUGO | S_IWUSR);
95072b91f9SBrian King MODULE_PARM_DESC(debug, "Enable driver debug information. "
96072b91f9SBrian King 		 "[Default=" __stringify(IBMVFC_DEBUG) "]");
97072b91f9SBrian King module_param_named(log_level, log_level, uint, 0);
98072b91f9SBrian King MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver. "
99072b91f9SBrian King 		 "[Default=" __stringify(IBMVFC_DEFAULT_LOG_LEVEL) "]");
100a6104b1eSTyrel Datwyler module_param_named(cls3_error, cls3_error, uint, 0);
1016c46301fSWei Yongjun MODULE_PARM_DESC(cls3_error, "Enable FC Class 3 Error Recovery. "
102a6104b1eSTyrel Datwyler 		 "[Default=" __stringify(IBMVFC_CLS3_ERROR) "]");
103072b91f9SBrian King 
104072b91f9SBrian King static const struct {
105072b91f9SBrian King 	u16 status;
106072b91f9SBrian King 	u16 error;
107072b91f9SBrian King 	u8 result;
108072b91f9SBrian King 	u8 retry;
109072b91f9SBrian King 	int log;
110072b91f9SBrian King 	char *name;
111072b91f9SBrian King } cmd_status [] = {
112072b91f9SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_UNABLE_TO_ESTABLISH, DID_ERROR, 1, 1, "unable to establish" },
113072b91f9SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_FAULT, DID_OK, 1, 0, "transport fault" },
114072b91f9SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_CMD_TIMEOUT, DID_TIME_OUT, 1, 1, "command timeout" },
115752b3232SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_ENETDOWN, DID_TRANSPORT_DISRUPTED, 1, 1, "network down" },
116072b91f9SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_HW_FAILURE, DID_ERROR, 1, 1, "hardware failure" },
117072b91f9SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_DOWN_ERR, DID_REQUEUE, 0, 0, "link down" },
118072b91f9SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_DEAD_ERR, DID_ERROR, 0, 0, "link dead" },
119072b91f9SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_UNABLE_TO_REGISTER, DID_ERROR, 1, 1, "unable to register" },
120072b91f9SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_BUSY, DID_BUS_BUSY, 1, 0, "transport busy" },
121072b91f9SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_DEAD, DID_ERROR, 0, 1, "transport dead" },
122072b91f9SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_CONFIG_ERROR, DID_ERROR, 1, 1, "configuration error" },
123072b91f9SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_NAME_SERVER_FAIL, DID_ERROR, 1, 1, "name server failure" },
124497f9c50SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 1, 0, "link halted" },
125072b91f9SBrian King 	{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_GENERAL, DID_OK, 1, 0, "general transport error" },
126072b91f9SBrian King 
127072b91f9SBrian King 	{ IBMVFC_VIOS_FAILURE, IBMVFC_CRQ_FAILURE, DID_REQUEUE, 1, 1, "CRQ failure" },
128072b91f9SBrian King 	{ IBMVFC_VIOS_FAILURE, IBMVFC_SW_FAILURE, DID_ERROR, 0, 1, "software failure" },
129752b3232SBrian King 	{ IBMVFC_VIOS_FAILURE, IBMVFC_INVALID_PARAMETER, DID_ERROR, 0, 1, "invalid parameter" },
130752b3232SBrian King 	{ IBMVFC_VIOS_FAILURE, IBMVFC_MISSING_PARAMETER, DID_ERROR, 0, 1, "missing parameter" },
131072b91f9SBrian King 	{ IBMVFC_VIOS_FAILURE, IBMVFC_HOST_IO_BUS, DID_ERROR, 1, 1, "host I/O bus failure" },
132752b3232SBrian King 	{ IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED, DID_ERROR, 0, 1, "transaction cancelled" },
133752b3232SBrian King 	{ IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED_IMPLICIT, DID_ERROR, 0, 1, "transaction cancelled implicit" },
134072b91f9SBrian King 	{ IBMVFC_VIOS_FAILURE, IBMVFC_INSUFFICIENT_RESOURCE, DID_REQUEUE, 1, 1, "insufficient resources" },
135646d3857SBrian King 	{ IBMVFC_VIOS_FAILURE, IBMVFC_PLOGI_REQUIRED, DID_ERROR, 0, 1, "port login required" },
136072b91f9SBrian King 	{ IBMVFC_VIOS_FAILURE, IBMVFC_COMMAND_FAILED, DID_ERROR, 1, 1, "command failed" },
137072b91f9SBrian King 
138072b91f9SBrian King 	{ IBMVFC_FC_FAILURE, IBMVFC_INVALID_ELS_CMD_CODE, DID_ERROR, 0, 1, "invalid ELS command code" },
139072b91f9SBrian King 	{ IBMVFC_FC_FAILURE, IBMVFC_INVALID_VERSION, DID_ERROR, 0, 1, "invalid version level" },
140072b91f9SBrian King 	{ IBMVFC_FC_FAILURE, IBMVFC_LOGICAL_ERROR, DID_ERROR, 1, 1, "logical error" },
141072b91f9SBrian King 	{ IBMVFC_FC_FAILURE, IBMVFC_INVALID_CT_IU_SIZE, DID_ERROR, 0, 1, "invalid CT_IU size" },
142072b91f9SBrian King 	{ IBMVFC_FC_FAILURE, IBMVFC_LOGICAL_BUSY, DID_REQUEUE, 1, 0, "logical busy" },
143072b91f9SBrian King 	{ IBMVFC_FC_FAILURE, IBMVFC_PROTOCOL_ERROR, DID_ERROR, 1, 1, "protocol error" },
144072b91f9SBrian King 	{ IBMVFC_FC_FAILURE, IBMVFC_UNABLE_TO_PERFORM_REQ, DID_ERROR, 1, 1, "unable to perform request" },
145072b91f9SBrian King 	{ IBMVFC_FC_FAILURE, IBMVFC_CMD_NOT_SUPPORTED, DID_ERROR, 0, 0, "command not supported" },
146072b91f9SBrian King 	{ IBMVFC_FC_FAILURE, IBMVFC_SERVER_NOT_AVAIL, DID_ERROR, 0, 1, "server not available" },
147072b91f9SBrian King 	{ IBMVFC_FC_FAILURE, IBMVFC_CMD_IN_PROGRESS, DID_ERROR, 0, 1, "command already in progress" },
148072b91f9SBrian King 	{ IBMVFC_FC_FAILURE, IBMVFC_VENDOR_SPECIFIC, DID_ERROR, 1, 1, "vendor specific" },
149072b91f9SBrian King 
150072b91f9SBrian King 	{ IBMVFC_FC_SCSI_ERROR, 0, DID_OK, 1, 0, "SCSI error" },
15195237c25STyrel Datwyler 	{ IBMVFC_FC_SCSI_ERROR, IBMVFC_COMMAND_FAILED, DID_ERROR, 0, 1, "PRLI to device failed." },
152072b91f9SBrian King };
153072b91f9SBrian King 
154072b91f9SBrian King static void ibmvfc_npiv_login(struct ibmvfc_host *);
155072b91f9SBrian King static void ibmvfc_tgt_send_prli(struct ibmvfc_target *);
156072b91f9SBrian King static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *);
157072b91f9SBrian King static void ibmvfc_tgt_query_target(struct ibmvfc_target *);
15879111d08SBrian King static void ibmvfc_npiv_logout(struct ibmvfc_host *);
159ed830385SBrian King static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *);
1604b29cb61SBrian King static void ibmvfc_tgt_move_login(struct ibmvfc_target *);
161072b91f9SBrian King 
16272ea7fe0STyrel Datwyler static void ibmvfc_dereg_sub_crqs(struct ibmvfc_host *);
16372ea7fe0STyrel Datwyler static void ibmvfc_reg_sub_crqs(struct ibmvfc_host *);
1645cf52964STyrel Datwyler 
165072b91f9SBrian King static const char *unknown_error = "unknown error";
166072b91f9SBrian King 
h_reg_sub_crq(unsigned long unit_address,unsigned long ioba,unsigned long length,unsigned long * cookie,unsigned long * irq)1679e6b6b81STyrel Datwyler static long h_reg_sub_crq(unsigned long unit_address, unsigned long ioba,
1689e6b6b81STyrel Datwyler 			  unsigned long length, unsigned long *cookie,
1699e6b6b81STyrel Datwyler 			  unsigned long *irq)
1709e6b6b81STyrel Datwyler {
1719e6b6b81STyrel Datwyler 	unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
1729e6b6b81STyrel Datwyler 	long rc;
1739e6b6b81STyrel Datwyler 
1749e6b6b81STyrel Datwyler 	rc = plpar_hcall(H_REG_SUB_CRQ, retbuf, unit_address, ioba, length);
1759e6b6b81STyrel Datwyler 	*cookie = retbuf[0];
1769e6b6b81STyrel Datwyler 	*irq = retbuf[1];
1779e6b6b81STyrel Datwyler 
1789e6b6b81STyrel Datwyler 	return rc;
1799e6b6b81STyrel Datwyler }
1809e6b6b81STyrel Datwyler 
ibmvfc_check_caps(struct ibmvfc_host * vhost,unsigned long cap_flags)181a318c2b7STyrel Datwyler static int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags)
182a318c2b7STyrel Datwyler {
183a318c2b7STyrel Datwyler 	u64 host_caps = be64_to_cpu(vhost->login_buf->resp.capabilities);
184a318c2b7STyrel Datwyler 
185a318c2b7STyrel Datwyler 	return (host_caps & cap_flags) ? 1 : 0;
186a318c2b7STyrel Datwyler }
187a318c2b7STyrel Datwyler 
ibmvfc_get_fcp_iu(struct ibmvfc_host * vhost,struct ibmvfc_cmd * vfc_cmd)1885a9d16f7STyrel Datwyler static struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost,
1895a9d16f7STyrel Datwyler 						   struct ibmvfc_cmd *vfc_cmd)
1905a9d16f7STyrel Datwyler {
1915a9d16f7STyrel Datwyler 	if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
1925a9d16f7STyrel Datwyler 		return &vfc_cmd->v2.iu;
1935a9d16f7STyrel Datwyler 	else
1945a9d16f7STyrel Datwyler 		return &vfc_cmd->v1.iu;
1955a9d16f7STyrel Datwyler }
1965a9d16f7STyrel Datwyler 
ibmvfc_get_fcp_rsp(struct ibmvfc_host * vhost,struct ibmvfc_cmd * vfc_cmd)1975a9d16f7STyrel Datwyler static struct ibmvfc_fcp_rsp *ibmvfc_get_fcp_rsp(struct ibmvfc_host *vhost,
1985a9d16f7STyrel Datwyler 						 struct ibmvfc_cmd *vfc_cmd)
1995a9d16f7STyrel Datwyler {
2005a9d16f7STyrel Datwyler 	if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
2015a9d16f7STyrel Datwyler 		return &vfc_cmd->v2.rsp;
2025a9d16f7STyrel Datwyler 	else
2035a9d16f7STyrel Datwyler 		return &vfc_cmd->v1.rsp;
2045a9d16f7STyrel Datwyler }
2055a9d16f7STyrel Datwyler 
206072b91f9SBrian King #ifdef CONFIG_SCSI_IBMVFC_TRACE
207072b91f9SBrian King /**
208072b91f9SBrian King  * ibmvfc_trc_start - Log a start trace entry
209072b91f9SBrian King  * @evt:		ibmvfc event struct
210072b91f9SBrian King  *
211072b91f9SBrian King  **/
ibmvfc_trc_start(struct ibmvfc_event * evt)212072b91f9SBrian King static void ibmvfc_trc_start(struct ibmvfc_event *evt)
213072b91f9SBrian King {
214072b91f9SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
215072b91f9SBrian King 	struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd;
216072b91f9SBrian King 	struct ibmvfc_mad_common *mad = &evt->iu.mad_common;
2175a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_cmd_iu *iu = ibmvfc_get_fcp_iu(vhost, vfc_cmd);
218072b91f9SBrian King 	struct ibmvfc_trace_entry *entry;
21957e80e0bSTyrel Datwyler 	int index = atomic_inc_return(&vhost->trace_index) & IBMVFC_TRACE_INDEX_MASK;
220072b91f9SBrian King 
22157e80e0bSTyrel Datwyler 	entry = &vhost->trace[index];
222072b91f9SBrian King 	entry->evt = evt;
223072b91f9SBrian King 	entry->time = jiffies;
224072b91f9SBrian King 	entry->fmt = evt->crq.format;
225072b91f9SBrian King 	entry->type = IBMVFC_TRC_START;
226072b91f9SBrian King 
227072b91f9SBrian King 	switch (entry->fmt) {
228072b91f9SBrian King 	case IBMVFC_CMD_FORMAT:
229c16b8a6dSTyrel Datwyler 		entry->op_code = iu->cdb[0];
2300aab6c3fSTyrel Datwyler 		entry->scsi_id = be64_to_cpu(vfc_cmd->tgt_scsi_id);
231c16b8a6dSTyrel Datwyler 		entry->lun = scsilun_to_int(&iu->lun);
232c16b8a6dSTyrel Datwyler 		entry->tmf_flags = iu->tmf_flags;
233c16b8a6dSTyrel Datwyler 		entry->u.start.xfer_len = be32_to_cpu(iu->xfer_len);
234072b91f9SBrian King 		break;
235072b91f9SBrian King 	case IBMVFC_MAD_FORMAT:
2360aab6c3fSTyrel Datwyler 		entry->op_code = be32_to_cpu(mad->opcode);
237072b91f9SBrian King 		break;
238072b91f9SBrian King 	default:
239072b91f9SBrian King 		break;
240f36cfe6aSChristopher Díaz Riveros 	}
241072b91f9SBrian King }
242072b91f9SBrian King 
243072b91f9SBrian King /**
244072b91f9SBrian King  * ibmvfc_trc_end - Log an end trace entry
245072b91f9SBrian King  * @evt:		ibmvfc event struct
246072b91f9SBrian King  *
247072b91f9SBrian King  **/
ibmvfc_trc_end(struct ibmvfc_event * evt)248072b91f9SBrian King static void ibmvfc_trc_end(struct ibmvfc_event *evt)
249072b91f9SBrian King {
250072b91f9SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
251072b91f9SBrian King 	struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
252072b91f9SBrian King 	struct ibmvfc_mad_common *mad = &evt->xfer_iu->mad_common;
2535a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_cmd_iu *iu = ibmvfc_get_fcp_iu(vhost, vfc_cmd);
2545a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd);
25557e80e0bSTyrel Datwyler 	struct ibmvfc_trace_entry *entry;
25657e80e0bSTyrel Datwyler 	int index = atomic_inc_return(&vhost->trace_index) & IBMVFC_TRACE_INDEX_MASK;
257072b91f9SBrian King 
25857e80e0bSTyrel Datwyler 	entry = &vhost->trace[index];
259072b91f9SBrian King 	entry->evt = evt;
260072b91f9SBrian King 	entry->time = jiffies;
261072b91f9SBrian King 	entry->fmt = evt->crq.format;
262072b91f9SBrian King 	entry->type = IBMVFC_TRC_END;
263072b91f9SBrian King 
264072b91f9SBrian King 	switch (entry->fmt) {
265072b91f9SBrian King 	case IBMVFC_CMD_FORMAT:
266c16b8a6dSTyrel Datwyler 		entry->op_code = iu->cdb[0];
2670aab6c3fSTyrel Datwyler 		entry->scsi_id = be64_to_cpu(vfc_cmd->tgt_scsi_id);
268c16b8a6dSTyrel Datwyler 		entry->lun = scsilun_to_int(&iu->lun);
269c16b8a6dSTyrel Datwyler 		entry->tmf_flags = iu->tmf_flags;
2700aab6c3fSTyrel Datwyler 		entry->u.end.status = be16_to_cpu(vfc_cmd->status);
2710aab6c3fSTyrel Datwyler 		entry->u.end.error = be16_to_cpu(vfc_cmd->error);
272c16b8a6dSTyrel Datwyler 		entry->u.end.fcp_rsp_flags = rsp->flags;
273c16b8a6dSTyrel Datwyler 		entry->u.end.rsp_code = rsp->data.info.rsp_code;
274c16b8a6dSTyrel Datwyler 		entry->u.end.scsi_status = rsp->scsi_status;
275072b91f9SBrian King 		break;
276072b91f9SBrian King 	case IBMVFC_MAD_FORMAT:
2770aab6c3fSTyrel Datwyler 		entry->op_code = be32_to_cpu(mad->opcode);
2780aab6c3fSTyrel Datwyler 		entry->u.end.status = be16_to_cpu(mad->status);
279072b91f9SBrian King 		break;
280072b91f9SBrian King 	default:
281072b91f9SBrian King 		break;
282072b91f9SBrian King 
283f36cfe6aSChristopher Díaz Riveros 	}
284072b91f9SBrian King }
285072b91f9SBrian King 
286072b91f9SBrian King #else
287072b91f9SBrian King #define ibmvfc_trc_start(evt) do { } while (0)
288072b91f9SBrian King #define ibmvfc_trc_end(evt) do { } while (0)
289072b91f9SBrian King #endif
290072b91f9SBrian King 
291072b91f9SBrian King /**
292072b91f9SBrian King  * ibmvfc_get_err_index - Find the index into cmd_status for the fcp response
293072b91f9SBrian King  * @status:		status / error class
294072b91f9SBrian King  * @error:		error
295072b91f9SBrian King  *
296072b91f9SBrian King  * Return value:
297072b91f9SBrian King  *	index into cmd_status / -EINVAL on failure
298072b91f9SBrian King  **/
ibmvfc_get_err_index(u16 status,u16 error)299072b91f9SBrian King static int ibmvfc_get_err_index(u16 status, u16 error)
300072b91f9SBrian King {
301072b91f9SBrian King 	int i;
302072b91f9SBrian King 
303072b91f9SBrian King 	for (i = 0; i < ARRAY_SIZE(cmd_status); i++)
304072b91f9SBrian King 		if ((cmd_status[i].status & status) == cmd_status[i].status &&
305072b91f9SBrian King 		    cmd_status[i].error == error)
306072b91f9SBrian King 			return i;
307072b91f9SBrian King 
308072b91f9SBrian King 	return -EINVAL;
309072b91f9SBrian King }
310072b91f9SBrian King 
311072b91f9SBrian King /**
312072b91f9SBrian King  * ibmvfc_get_cmd_error - Find the error description for the fcp response
313072b91f9SBrian King  * @status:		status / error class
314072b91f9SBrian King  * @error:		error
315072b91f9SBrian King  *
316072b91f9SBrian King  * Return value:
317072b91f9SBrian King  *	error description string
318072b91f9SBrian King  **/
ibmvfc_get_cmd_error(u16 status,u16 error)319072b91f9SBrian King static const char *ibmvfc_get_cmd_error(u16 status, u16 error)
320072b91f9SBrian King {
321072b91f9SBrian King 	int rc = ibmvfc_get_err_index(status, error);
322072b91f9SBrian King 	if (rc >= 0)
323072b91f9SBrian King 		return cmd_status[rc].name;
324072b91f9SBrian King 	return unknown_error;
325072b91f9SBrian King }
326072b91f9SBrian King 
327072b91f9SBrian King /**
328072b91f9SBrian King  * ibmvfc_get_err_result - Find the scsi status to return for the fcp response
329dd9c7729SLee Jones  * @vhost:      ibmvfc host struct
330072b91f9SBrian King  * @vfc_cmd:	ibmvfc command struct
331072b91f9SBrian King  *
332072b91f9SBrian King  * Return value:
333072b91f9SBrian King  *	SCSI result value to return for completed command
334072b91f9SBrian King  **/
ibmvfc_get_err_result(struct ibmvfc_host * vhost,struct ibmvfc_cmd * vfc_cmd)3355a9d16f7STyrel Datwyler static int ibmvfc_get_err_result(struct ibmvfc_host *vhost, struct ibmvfc_cmd *vfc_cmd)
336072b91f9SBrian King {
337072b91f9SBrian King 	int err;
3385a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd);
3390aab6c3fSTyrel Datwyler 	int fc_rsp_len = be32_to_cpu(rsp->fcp_rsp_len);
340072b91f9SBrian King 
341072b91f9SBrian King 	if ((rsp->flags & FCP_RSP_LEN_VALID) &&
3424a2837d4SBrian King 	    ((fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) ||
343072b91f9SBrian King 	     rsp->data.info.rsp_code))
344072b91f9SBrian King 		return DID_ERROR << 16;
345072b91f9SBrian King 
3460aab6c3fSTyrel Datwyler 	err = ibmvfc_get_err_index(be16_to_cpu(vfc_cmd->status), be16_to_cpu(vfc_cmd->error));
347072b91f9SBrian King 	if (err >= 0)
348072b91f9SBrian King 		return rsp->scsi_status | (cmd_status[err].result << 16);
349072b91f9SBrian King 	return rsp->scsi_status | (DID_ERROR << 16);
350072b91f9SBrian King }
351072b91f9SBrian King 
352072b91f9SBrian King /**
353072b91f9SBrian King  * ibmvfc_retry_cmd - Determine if error status is retryable
354072b91f9SBrian King  * @status:		status / error class
355072b91f9SBrian King  * @error:		error
356072b91f9SBrian King  *
357072b91f9SBrian King  * Return value:
358072b91f9SBrian King  *	1 if error should be retried / 0 if it should not
359072b91f9SBrian King  **/
ibmvfc_retry_cmd(u16 status,u16 error)360072b91f9SBrian King static int ibmvfc_retry_cmd(u16 status, u16 error)
361072b91f9SBrian King {
362072b91f9SBrian King 	int rc = ibmvfc_get_err_index(status, error);
363072b91f9SBrian King 
364072b91f9SBrian King 	if (rc >= 0)
365072b91f9SBrian King 		return cmd_status[rc].retry;
366072b91f9SBrian King 	return 1;
367072b91f9SBrian King }
368072b91f9SBrian King 
369072b91f9SBrian King static const char *unknown_fc_explain = "unknown fc explain";
370072b91f9SBrian King 
371072b91f9SBrian King static const struct {
372072b91f9SBrian King 	u16 fc_explain;
373072b91f9SBrian King 	char *name;
374072b91f9SBrian King } ls_explain [] = {
375072b91f9SBrian King 	{ 0x00, "no additional explanation" },
376072b91f9SBrian King 	{ 0x01, "service parameter error - options" },
377072b91f9SBrian King 	{ 0x03, "service parameter error - initiator control" },
378072b91f9SBrian King 	{ 0x05, "service parameter error - recipient control" },
379072b91f9SBrian King 	{ 0x07, "service parameter error - received data field size" },
380072b91f9SBrian King 	{ 0x09, "service parameter error - concurrent seq" },
381072b91f9SBrian King 	{ 0x0B, "service parameter error - credit" },
382072b91f9SBrian King 	{ 0x0D, "invalid N_Port/F_Port_Name" },
383072b91f9SBrian King 	{ 0x0E, "invalid node/Fabric Name" },
384072b91f9SBrian King 	{ 0x0F, "invalid common service parameters" },
385072b91f9SBrian King 	{ 0x11, "invalid association header" },
386072b91f9SBrian King 	{ 0x13, "association header required" },
387072b91f9SBrian King 	{ 0x15, "invalid originator S_ID" },
388072b91f9SBrian King 	{ 0x17, "invalid OX_ID-RX-ID combination" },
389072b91f9SBrian King 	{ 0x19, "command (request) already in progress" },
390072b91f9SBrian King 	{ 0x1E, "N_Port Login requested" },
391072b91f9SBrian King 	{ 0x1F, "Invalid N_Port_ID" },
392072b91f9SBrian King };
393072b91f9SBrian King 
394072b91f9SBrian King static const struct {
395072b91f9SBrian King 	u16 fc_explain;
396072b91f9SBrian King 	char *name;
397072b91f9SBrian King } gs_explain [] = {
398072b91f9SBrian King 	{ 0x00, "no additional explanation" },
399072b91f9SBrian King 	{ 0x01, "port identifier not registered" },
400072b91f9SBrian King 	{ 0x02, "port name not registered" },
401072b91f9SBrian King 	{ 0x03, "node name not registered" },
402072b91f9SBrian King 	{ 0x04, "class of service not registered" },
403072b91f9SBrian King 	{ 0x06, "initial process associator not registered" },
404072b91f9SBrian King 	{ 0x07, "FC-4 TYPEs not registered" },
405072b91f9SBrian King 	{ 0x08, "symbolic port name not registered" },
406072b91f9SBrian King 	{ 0x09, "symbolic node name not registered" },
407072b91f9SBrian King 	{ 0x0A, "port type not registered" },
408072b91f9SBrian King 	{ 0xF0, "authorization exception" },
409072b91f9SBrian King 	{ 0xF1, "authentication exception" },
410072b91f9SBrian King 	{ 0xF2, "data base full" },
411072b91f9SBrian King 	{ 0xF3, "data base empty" },
412072b91f9SBrian King 	{ 0xF4, "processing request" },
413072b91f9SBrian King 	{ 0xF5, "unable to verify connection" },
414072b91f9SBrian King 	{ 0xF6, "devices not in a common zone" },
415072b91f9SBrian King };
416072b91f9SBrian King 
417072b91f9SBrian King /**
418072b91f9SBrian King  * ibmvfc_get_ls_explain - Return the FC Explain description text
419072b91f9SBrian King  * @status:	FC Explain status
420072b91f9SBrian King  *
421072b91f9SBrian King  * Returns:
422072b91f9SBrian King  *	error string
423072b91f9SBrian King  **/
ibmvfc_get_ls_explain(u16 status)424072b91f9SBrian King static const char *ibmvfc_get_ls_explain(u16 status)
425072b91f9SBrian King {
426072b91f9SBrian King 	int i;
427072b91f9SBrian King 
428072b91f9SBrian King 	for (i = 0; i < ARRAY_SIZE(ls_explain); i++)
429072b91f9SBrian King 		if (ls_explain[i].fc_explain == status)
430072b91f9SBrian King 			return ls_explain[i].name;
431072b91f9SBrian King 
432072b91f9SBrian King 	return unknown_fc_explain;
433072b91f9SBrian King }
434072b91f9SBrian King 
435072b91f9SBrian King /**
436072b91f9SBrian King  * ibmvfc_get_gs_explain - Return the FC Explain description text
437072b91f9SBrian King  * @status:	FC Explain status
438072b91f9SBrian King  *
439072b91f9SBrian King  * Returns:
440072b91f9SBrian King  *	error string
441072b91f9SBrian King  **/
ibmvfc_get_gs_explain(u16 status)442072b91f9SBrian King static const char *ibmvfc_get_gs_explain(u16 status)
443072b91f9SBrian King {
444072b91f9SBrian King 	int i;
445072b91f9SBrian King 
446072b91f9SBrian King 	for (i = 0; i < ARRAY_SIZE(gs_explain); i++)
447072b91f9SBrian King 		if (gs_explain[i].fc_explain == status)
448072b91f9SBrian King 			return gs_explain[i].name;
449072b91f9SBrian King 
450072b91f9SBrian King 	return unknown_fc_explain;
451072b91f9SBrian King }
452072b91f9SBrian King 
453072b91f9SBrian King static const struct {
454072b91f9SBrian King 	enum ibmvfc_fc_type fc_type;
455072b91f9SBrian King 	char *name;
456072b91f9SBrian King } fc_type [] = {
457072b91f9SBrian King 	{ IBMVFC_FABRIC_REJECT, "fabric reject" },
458072b91f9SBrian King 	{ IBMVFC_PORT_REJECT, "port reject" },
459072b91f9SBrian King 	{ IBMVFC_LS_REJECT, "ELS reject" },
460072b91f9SBrian King 	{ IBMVFC_FABRIC_BUSY, "fabric busy" },
461072b91f9SBrian King 	{ IBMVFC_PORT_BUSY, "port busy" },
462072b91f9SBrian King 	{ IBMVFC_BASIC_REJECT, "basic reject" },
463072b91f9SBrian King };
464072b91f9SBrian King 
465072b91f9SBrian King static const char *unknown_fc_type = "unknown fc type";
466072b91f9SBrian King 
467072b91f9SBrian King /**
468072b91f9SBrian King  * ibmvfc_get_fc_type - Return the FC Type description text
469072b91f9SBrian King  * @status:	FC Type error status
470072b91f9SBrian King  *
471072b91f9SBrian King  * Returns:
472072b91f9SBrian King  *	error string
473072b91f9SBrian King  **/
ibmvfc_get_fc_type(u16 status)474072b91f9SBrian King static const char *ibmvfc_get_fc_type(u16 status)
475072b91f9SBrian King {
476072b91f9SBrian King 	int i;
477072b91f9SBrian King 
478072b91f9SBrian King 	for (i = 0; i < ARRAY_SIZE(fc_type); i++)
479072b91f9SBrian King 		if (fc_type[i].fc_type == status)
480072b91f9SBrian King 			return fc_type[i].name;
481072b91f9SBrian King 
482072b91f9SBrian King 	return unknown_fc_type;
483072b91f9SBrian King }
484072b91f9SBrian King 
485072b91f9SBrian King /**
486072b91f9SBrian King  * ibmvfc_set_tgt_action - Set the next init action for the target
487072b91f9SBrian King  * @tgt:		ibmvfc target struct
488072b91f9SBrian King  * @action:		action to perform
489072b91f9SBrian King  *
490ed830385SBrian King  * Returns:
491ed830385SBrian King  *	0 if action changed / non-zero if not changed
492072b91f9SBrian King  **/
ibmvfc_set_tgt_action(struct ibmvfc_target * tgt,enum ibmvfc_target_action action)493ed830385SBrian King static int ibmvfc_set_tgt_action(struct ibmvfc_target *tgt,
494072b91f9SBrian King 				  enum ibmvfc_target_action action)
495072b91f9SBrian King {
496ed830385SBrian King 	int rc = -EINVAL;
497ed830385SBrian King 
498072b91f9SBrian King 	switch (tgt->action) {
499ed830385SBrian King 	case IBMVFC_TGT_ACTION_LOGOUT_RPORT:
500ed830385SBrian King 		if (action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT ||
501ed830385SBrian King 		    action == IBMVFC_TGT_ACTION_DEL_RPORT) {
502d5da3040SBrian King 			tgt->action = action;
503ed830385SBrian King 			rc = 0;
504ed830385SBrian King 		}
505ed830385SBrian King 		break;
506ed830385SBrian King 	case IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT:
5074b29cb61SBrian King 		if (action == IBMVFC_TGT_ACTION_DEL_RPORT ||
5084b29cb61SBrian King 		    action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
5094b29cb61SBrian King 			tgt->action = action;
5104b29cb61SBrian King 			rc = 0;
5114b29cb61SBrian King 		}
5124b29cb61SBrian King 		break;
5134b29cb61SBrian King 	case IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT:
5144b29cb61SBrian King 		if (action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
5154b29cb61SBrian King 			tgt->action = action;
5164b29cb61SBrian King 			rc = 0;
5174b29cb61SBrian King 		}
5184b29cb61SBrian King 		break;
5194b29cb61SBrian King 	case IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT:
5204b29cb61SBrian King 		if (action == IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT) {
521ed830385SBrian King 			tgt->action = action;
522ed830385SBrian King 			rc = 0;
523ed830385SBrian King 		}
524ed830385SBrian King 		break;
525ed830385SBrian King 	case IBMVFC_TGT_ACTION_DEL_RPORT:
526ed830385SBrian King 		if (action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
527ed830385SBrian King 			tgt->action = action;
528ed830385SBrian King 			rc = 0;
529ed830385SBrian King 		}
5304b29cb61SBrian King 		break;
531d5da3040SBrian King 	case IBMVFC_TGT_ACTION_DELETED_RPORT:
532072b91f9SBrian King 		break;
533072b91f9SBrian King 	default:
534072b91f9SBrian King 		tgt->action = action;
535ed830385SBrian King 		rc = 0;
536072b91f9SBrian King 		break;
537072b91f9SBrian King 	}
538ed830385SBrian King 
5394b29cb61SBrian King 	if (action >= IBMVFC_TGT_ACTION_LOGOUT_RPORT)
5404b29cb61SBrian King 		tgt->add_rport = 0;
5414b29cb61SBrian King 
542ed830385SBrian King 	return rc;
543072b91f9SBrian King }
544072b91f9SBrian King 
545072b91f9SBrian King /**
546072b91f9SBrian King  * ibmvfc_set_host_state - Set the state for the host
547072b91f9SBrian King  * @vhost:		ibmvfc host struct
548072b91f9SBrian King  * @state:		state to set host to
549072b91f9SBrian King  *
550072b91f9SBrian King  * Returns:
551072b91f9SBrian King  *	0 if state changed / non-zero if not changed
552072b91f9SBrian King  **/
ibmvfc_set_host_state(struct ibmvfc_host * vhost,enum ibmvfc_host_state state)553072b91f9SBrian King static int ibmvfc_set_host_state(struct ibmvfc_host *vhost,
554072b91f9SBrian King 				  enum ibmvfc_host_state state)
555072b91f9SBrian King {
556072b91f9SBrian King 	int rc = 0;
557072b91f9SBrian King 
558072b91f9SBrian King 	switch (vhost->state) {
559072b91f9SBrian King 	case IBMVFC_HOST_OFFLINE:
560072b91f9SBrian King 		rc = -EINVAL;
561072b91f9SBrian King 		break;
562072b91f9SBrian King 	default:
563072b91f9SBrian King 		vhost->state = state;
564072b91f9SBrian King 		break;
565f36cfe6aSChristopher Díaz Riveros 	}
566072b91f9SBrian King 
567072b91f9SBrian King 	return rc;
568072b91f9SBrian King }
569072b91f9SBrian King 
570072b91f9SBrian King /**
571072b91f9SBrian King  * ibmvfc_set_host_action - Set the next init action for the host
572072b91f9SBrian King  * @vhost:		ibmvfc host struct
573072b91f9SBrian King  * @action:		action to perform
574072b91f9SBrian King  *
575072b91f9SBrian King  **/
ibmvfc_set_host_action(struct ibmvfc_host * vhost,enum ibmvfc_host_action action)576072b91f9SBrian King static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
577072b91f9SBrian King 				   enum ibmvfc_host_action action)
578072b91f9SBrian King {
579072b91f9SBrian King 	switch (action) {
580072b91f9SBrian King 	case IBMVFC_HOST_ACTION_ALLOC_TGTS:
581072b91f9SBrian King 		if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT)
582072b91f9SBrian King 			vhost->action = action;
583072b91f9SBrian King 		break;
58479111d08SBrian King 	case IBMVFC_HOST_ACTION_LOGO_WAIT:
58579111d08SBrian King 		if (vhost->action == IBMVFC_HOST_ACTION_LOGO)
58679111d08SBrian King 			vhost->action = action;
58779111d08SBrian King 		break;
588072b91f9SBrian King 	case IBMVFC_HOST_ACTION_INIT_WAIT:
589072b91f9SBrian King 		if (vhost->action == IBMVFC_HOST_ACTION_INIT)
590072b91f9SBrian King 			vhost->action = action;
591072b91f9SBrian King 		break;
592072b91f9SBrian King 	case IBMVFC_HOST_ACTION_QUERY:
593072b91f9SBrian King 		switch (vhost->action) {
594072b91f9SBrian King 		case IBMVFC_HOST_ACTION_INIT_WAIT:
595072b91f9SBrian King 		case IBMVFC_HOST_ACTION_NONE:
59643c8da90SBrian King 		case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
597072b91f9SBrian King 			vhost->action = action;
598072b91f9SBrian King 			break;
599072b91f9SBrian King 		default:
600072b91f9SBrian King 			break;
601f36cfe6aSChristopher Díaz Riveros 		}
602072b91f9SBrian King 		break;
603072b91f9SBrian King 	case IBMVFC_HOST_ACTION_TGT_INIT:
604072b91f9SBrian King 		if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS)
605072b91f9SBrian King 			vhost->action = action;
606072b91f9SBrian King 		break;
60715cfef86SBrian King 	case IBMVFC_HOST_ACTION_REENABLE:
60815cfef86SBrian King 	case IBMVFC_HOST_ACTION_RESET:
60915cfef86SBrian King 		vhost->action = action;
61015cfef86SBrian King 		break;
611072b91f9SBrian King 	case IBMVFC_HOST_ACTION_INIT:
612072b91f9SBrian King 	case IBMVFC_HOST_ACTION_TGT_DEL:
61315cfef86SBrian King 	case IBMVFC_HOST_ACTION_LOGO:
61415cfef86SBrian King 	case IBMVFC_HOST_ACTION_QUERY_TGTS:
61515cfef86SBrian King 	case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
61615cfef86SBrian King 	case IBMVFC_HOST_ACTION_NONE:
61715cfef86SBrian King 	default:
61873ee5d86SBrian King 		switch (vhost->action) {
61973ee5d86SBrian King 		case IBMVFC_HOST_ACTION_RESET:
62073ee5d86SBrian King 		case IBMVFC_HOST_ACTION_REENABLE:
62173ee5d86SBrian King 			break;
62273ee5d86SBrian King 		default:
62373ee5d86SBrian King 			vhost->action = action;
62473ee5d86SBrian King 			break;
625f36cfe6aSChristopher Díaz Riveros 		}
62673ee5d86SBrian King 		break;
627f36cfe6aSChristopher Díaz Riveros 	}
628072b91f9SBrian King }
629072b91f9SBrian King 
630072b91f9SBrian King /**
631072b91f9SBrian King  * ibmvfc_reinit_host - Re-start host initialization (no NPIV Login)
632072b91f9SBrian King  * @vhost:		ibmvfc host struct
633072b91f9SBrian King  *
634072b91f9SBrian King  * Return value:
635072b91f9SBrian King  *	nothing
636072b91f9SBrian King  **/
ibmvfc_reinit_host(struct ibmvfc_host * vhost)637072b91f9SBrian King static void ibmvfc_reinit_host(struct ibmvfc_host *vhost)
638072b91f9SBrian King {
6394b29cb61SBrian King 	if (vhost->action == IBMVFC_HOST_ACTION_NONE &&
6404b29cb61SBrian King 	    vhost->state == IBMVFC_ACTIVE) {
6412d0da2a4SBrian King 		if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
642072b91f9SBrian King 			scsi_block_requests(vhost->host);
643072b91f9SBrian King 			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
6442d0da2a4SBrian King 		}
645072b91f9SBrian King 	} else
646072b91f9SBrian King 		vhost->reinit = 1;
647072b91f9SBrian King 
648072b91f9SBrian King 	wake_up(&vhost->work_wait_q);
649072b91f9SBrian King }
650072b91f9SBrian King 
651072b91f9SBrian King /**
652ed830385SBrian King  * ibmvfc_del_tgt - Schedule cleanup and removal of the target
653ed830385SBrian King  * @tgt:		ibmvfc target struct
654ed830385SBrian King  **/
ibmvfc_del_tgt(struct ibmvfc_target * tgt)655ed830385SBrian King static void ibmvfc_del_tgt(struct ibmvfc_target *tgt)
656ed830385SBrian King {
6572e51f78bSBrian King 	if (!ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_RPORT)) {
658ed830385SBrian King 		tgt->job_step = ibmvfc_tgt_implicit_logout_and_del;
6592e51f78bSBrian King 		tgt->init_retries = 0;
6602e51f78bSBrian King 	}
661ed830385SBrian King 	wake_up(&tgt->vhost->work_wait_q);
662ed830385SBrian King }
663ed830385SBrian King 
664ed830385SBrian King /**
665072b91f9SBrian King  * ibmvfc_link_down - Handle a link down event from the adapter
666072b91f9SBrian King  * @vhost:	ibmvfc host struct
667072b91f9SBrian King  * @state:	ibmvfc host state to enter
668072b91f9SBrian King  *
669072b91f9SBrian King  **/
ibmvfc_link_down(struct ibmvfc_host * vhost,enum ibmvfc_host_state state)670072b91f9SBrian King static void ibmvfc_link_down(struct ibmvfc_host *vhost,
671072b91f9SBrian King 			     enum ibmvfc_host_state state)
672072b91f9SBrian King {
673072b91f9SBrian King 	struct ibmvfc_target *tgt;
674072b91f9SBrian King 
675072b91f9SBrian King 	ENTER;
676072b91f9SBrian King 	scsi_block_requests(vhost->host);
677072b91f9SBrian King 	list_for_each_entry(tgt, &vhost->targets, queue)
678ed830385SBrian King 		ibmvfc_del_tgt(tgt);
679072b91f9SBrian King 	ibmvfc_set_host_state(vhost, state);
680072b91f9SBrian King 	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
681072b91f9SBrian King 	vhost->events_to_log |= IBMVFC_AE_LINKDOWN;
682072b91f9SBrian King 	wake_up(&vhost->work_wait_q);
683072b91f9SBrian King 	LEAVE;
684072b91f9SBrian King }
685072b91f9SBrian King 
686072b91f9SBrian King /**
687072b91f9SBrian King  * ibmvfc_init_host - Start host initialization
688072b91f9SBrian King  * @vhost:		ibmvfc host struct
689072b91f9SBrian King  *
690072b91f9SBrian King  * Return value:
691072b91f9SBrian King  *	nothing
692072b91f9SBrian King  **/
ibmvfc_init_host(struct ibmvfc_host * vhost)693861890c6SBrian King static void ibmvfc_init_host(struct ibmvfc_host *vhost)
694072b91f9SBrian King {
695072b91f9SBrian King 	struct ibmvfc_target *tgt;
696072b91f9SBrian King 
697072b91f9SBrian King 	if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
6981c41fa82SBrian King 		if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
699072b91f9SBrian King 			dev_err(vhost->dev,
700072b91f9SBrian King 				"Host initialization retries exceeded. Taking adapter offline\n");
701072b91f9SBrian King 			ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
702072b91f9SBrian King 			return;
703072b91f9SBrian King 		}
704072b91f9SBrian King 	}
705072b91f9SBrian King 
706072b91f9SBrian King 	if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
707f8968665STyrel Datwyler 		memset(vhost->async_crq.msgs.async, 0, PAGE_SIZE);
708cf6f10d7SBrian King 		vhost->async_crq.cur = 0;
709cf6f10d7SBrian King 
71062fa3ce0SBrian King 		list_for_each_entry(tgt, &vhost->targets, queue) {
71162fa3ce0SBrian King 			if (vhost->client_migrated)
71262fa3ce0SBrian King 				tgt->need_login = 1;
71362fa3ce0SBrian King 			else
714ed830385SBrian King 				ibmvfc_del_tgt(tgt);
71562fa3ce0SBrian King 		}
71662fa3ce0SBrian King 
717072b91f9SBrian King 		scsi_block_requests(vhost->host);
718072b91f9SBrian King 		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
719072b91f9SBrian King 		vhost->job_step = ibmvfc_npiv_login;
720072b91f9SBrian King 		wake_up(&vhost->work_wait_q);
721072b91f9SBrian King 	}
722072b91f9SBrian King }
723072b91f9SBrian King 
724072b91f9SBrian King /**
725072b91f9SBrian King  * ibmvfc_send_crq - Send a CRQ
726072b91f9SBrian King  * @vhost:	ibmvfc host struct
727072b91f9SBrian King  * @word1:	the first 64 bits of the data
728072b91f9SBrian King  * @word2:	the second 64 bits of the data
729072b91f9SBrian King  *
730072b91f9SBrian King  * Return value:
731072b91f9SBrian King  *	0 on success / other on failure
732072b91f9SBrian King  **/
ibmvfc_send_crq(struct ibmvfc_host * vhost,u64 word1,u64 word2)733072b91f9SBrian King static int ibmvfc_send_crq(struct ibmvfc_host *vhost, u64 word1, u64 word2)
734072b91f9SBrian King {
735072b91f9SBrian King 	struct vio_dev *vdev = to_vio_dev(vhost->dev);
736072b91f9SBrian King 	return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2);
737072b91f9SBrian King }
738072b91f9SBrian King 
ibmvfc_send_sub_crq(struct ibmvfc_host * vhost,u64 cookie,u64 word1,u64 word2,u64 word3,u64 word4)73931750fbdSTyrel Datwyler static int ibmvfc_send_sub_crq(struct ibmvfc_host *vhost, u64 cookie, u64 word1,
74031750fbdSTyrel Datwyler 			       u64 word2, u64 word3, u64 word4)
74131750fbdSTyrel Datwyler {
74231750fbdSTyrel Datwyler 	struct vio_dev *vdev = to_vio_dev(vhost->dev);
74331750fbdSTyrel Datwyler 
74431750fbdSTyrel Datwyler 	return plpar_hcall_norets(H_SEND_SUB_CRQ, vdev->unit_address, cookie,
74531750fbdSTyrel Datwyler 				  word1, word2, word3, word4);
74631750fbdSTyrel Datwyler }
74731750fbdSTyrel Datwyler 
748072b91f9SBrian King /**
749072b91f9SBrian King  * ibmvfc_send_crq_init - Send a CRQ init message
750072b91f9SBrian King  * @vhost:	ibmvfc host struct
751072b91f9SBrian King  *
752072b91f9SBrian King  * Return value:
753072b91f9SBrian King  *	0 on success / other on failure
754072b91f9SBrian King  **/
ibmvfc_send_crq_init(struct ibmvfc_host * vhost)755072b91f9SBrian King static int ibmvfc_send_crq_init(struct ibmvfc_host *vhost)
756072b91f9SBrian King {
757072b91f9SBrian King 	ibmvfc_dbg(vhost, "Sending CRQ init\n");
758072b91f9SBrian King 	return ibmvfc_send_crq(vhost, 0xC001000000000000LL, 0);
759072b91f9SBrian King }
760072b91f9SBrian King 
761072b91f9SBrian King /**
762072b91f9SBrian King  * ibmvfc_send_crq_init_complete - Send a CRQ init complete message
763072b91f9SBrian King  * @vhost:	ibmvfc host struct
764072b91f9SBrian King  *
765072b91f9SBrian King  * Return value:
766072b91f9SBrian King  *	0 on success / other on failure
767072b91f9SBrian King  **/
ibmvfc_send_crq_init_complete(struct ibmvfc_host * vhost)768072b91f9SBrian King static int ibmvfc_send_crq_init_complete(struct ibmvfc_host *vhost)
769072b91f9SBrian King {
770072b91f9SBrian King 	ibmvfc_dbg(vhost, "Sending CRQ init complete\n");
771072b91f9SBrian King 	return ibmvfc_send_crq(vhost, 0xC002000000000000LL, 0);
772072b91f9SBrian King }
773072b91f9SBrian King 
774072b91f9SBrian King /**
775225acf5fSTyrel Datwyler  * ibmvfc_init_event_pool - Allocates and initializes the event pool for a host
776225acf5fSTyrel Datwyler  * @vhost:	ibmvfc host who owns the event pool
777dd9c7729SLee Jones  * @queue:      ibmvfc queue struct
778dd9c7729SLee Jones  * @size:       pool size
779225acf5fSTyrel Datwyler  *
780225acf5fSTyrel Datwyler  * Returns zero on success.
781225acf5fSTyrel Datwyler  **/
ibmvfc_init_event_pool(struct ibmvfc_host * vhost,struct ibmvfc_queue * queue,unsigned int size)782225acf5fSTyrel Datwyler static int ibmvfc_init_event_pool(struct ibmvfc_host *vhost,
783bb35ecb2STyrel Datwyler 				  struct ibmvfc_queue *queue,
784bb35ecb2STyrel Datwyler 				  unsigned int size)
785225acf5fSTyrel Datwyler {
786225acf5fSTyrel Datwyler 	int i;
787225acf5fSTyrel Datwyler 	struct ibmvfc_event_pool *pool = &queue->evt_pool;
788225acf5fSTyrel Datwyler 
789225acf5fSTyrel Datwyler 	ENTER;
790bb35ecb2STyrel Datwyler 	if (!size)
791bb35ecb2STyrel Datwyler 		return 0;
792bb35ecb2STyrel Datwyler 
793bb35ecb2STyrel Datwyler 	pool->size = size;
794bb35ecb2STyrel Datwyler 	pool->events = kcalloc(size, sizeof(*pool->events), GFP_KERNEL);
795225acf5fSTyrel Datwyler 	if (!pool->events)
796225acf5fSTyrel Datwyler 		return -ENOMEM;
797225acf5fSTyrel Datwyler 
798225acf5fSTyrel Datwyler 	pool->iu_storage = dma_alloc_coherent(vhost->dev,
799bb35ecb2STyrel Datwyler 					      size * sizeof(*pool->iu_storage),
800225acf5fSTyrel Datwyler 					      &pool->iu_token, 0);
801225acf5fSTyrel Datwyler 
802225acf5fSTyrel Datwyler 	if (!pool->iu_storage) {
803225acf5fSTyrel Datwyler 		kfree(pool->events);
804225acf5fSTyrel Datwyler 		return -ENOMEM;
805225acf5fSTyrel Datwyler 	}
806225acf5fSTyrel Datwyler 
807225acf5fSTyrel Datwyler 	INIT_LIST_HEAD(&queue->sent);
808225acf5fSTyrel Datwyler 	INIT_LIST_HEAD(&queue->free);
809225acf5fSTyrel Datwyler 	spin_lock_init(&queue->l_lock);
810225acf5fSTyrel Datwyler 
811bb35ecb2STyrel Datwyler 	for (i = 0; i < size; ++i) {
812225acf5fSTyrel Datwyler 		struct ibmvfc_event *evt = &pool->events[i];
813225acf5fSTyrel Datwyler 
814a264cf5eSTyrel Datwyler 		/*
815a264cf5eSTyrel Datwyler 		 * evt->active states
816a264cf5eSTyrel Datwyler 		 *  1 = in flight
817a264cf5eSTyrel Datwyler 		 *  0 = being completed
818a264cf5eSTyrel Datwyler 		 * -1 = free/freed
819a264cf5eSTyrel Datwyler 		 */
820a264cf5eSTyrel Datwyler 		atomic_set(&evt->active, -1);
821225acf5fSTyrel Datwyler 		atomic_set(&evt->free, 1);
822225acf5fSTyrel Datwyler 		evt->crq.valid = 0x80;
823225acf5fSTyrel Datwyler 		evt->crq.ioba = cpu_to_be64(pool->iu_token + (sizeof(*evt->xfer_iu) * i));
824225acf5fSTyrel Datwyler 		evt->xfer_iu = pool->iu_storage + i;
825225acf5fSTyrel Datwyler 		evt->vhost = vhost;
826225acf5fSTyrel Datwyler 		evt->queue = queue;
827225acf5fSTyrel Datwyler 		evt->ext_list = NULL;
828225acf5fSTyrel Datwyler 		list_add_tail(&evt->queue_list, &queue->free);
829225acf5fSTyrel Datwyler 	}
830225acf5fSTyrel Datwyler 
831225acf5fSTyrel Datwyler 	LEAVE;
832225acf5fSTyrel Datwyler 	return 0;
833225acf5fSTyrel Datwyler }
834225acf5fSTyrel Datwyler 
835225acf5fSTyrel Datwyler /**
836225acf5fSTyrel Datwyler  * ibmvfc_free_event_pool - Frees memory of the event pool of a host
837225acf5fSTyrel Datwyler  * @vhost:	ibmvfc host who owns the event pool
838dd9c7729SLee Jones  * @queue:      ibmvfc queue struct
839225acf5fSTyrel Datwyler  *
840225acf5fSTyrel Datwyler  **/
ibmvfc_free_event_pool(struct ibmvfc_host * vhost,struct ibmvfc_queue * queue)841225acf5fSTyrel Datwyler static void ibmvfc_free_event_pool(struct ibmvfc_host *vhost,
842225acf5fSTyrel Datwyler 				   struct ibmvfc_queue *queue)
843225acf5fSTyrel Datwyler {
844225acf5fSTyrel Datwyler 	int i;
845225acf5fSTyrel Datwyler 	struct ibmvfc_event_pool *pool = &queue->evt_pool;
846225acf5fSTyrel Datwyler 
847225acf5fSTyrel Datwyler 	ENTER;
848225acf5fSTyrel Datwyler 	for (i = 0; i < pool->size; ++i) {
849225acf5fSTyrel Datwyler 		list_del(&pool->events[i].queue_list);
850225acf5fSTyrel Datwyler 		BUG_ON(atomic_read(&pool->events[i].free) != 1);
851225acf5fSTyrel Datwyler 		if (pool->events[i].ext_list)
852225acf5fSTyrel Datwyler 			dma_pool_free(vhost->sg_pool,
853225acf5fSTyrel Datwyler 				      pool->events[i].ext_list,
854225acf5fSTyrel Datwyler 				      pool->events[i].ext_list_token);
855225acf5fSTyrel Datwyler 	}
856225acf5fSTyrel Datwyler 
857225acf5fSTyrel Datwyler 	kfree(pool->events);
858225acf5fSTyrel Datwyler 	dma_free_coherent(vhost->dev,
859225acf5fSTyrel Datwyler 			  pool->size * sizeof(*pool->iu_storage),
860225acf5fSTyrel Datwyler 			  pool->iu_storage, pool->iu_token);
861225acf5fSTyrel Datwyler 	LEAVE;
862225acf5fSTyrel Datwyler }
863225acf5fSTyrel Datwyler 
864225acf5fSTyrel Datwyler /**
865f8968665STyrel Datwyler  * ibmvfc_free_queue - Deallocate queue
866f8968665STyrel Datwyler  * @vhost:	ibmvfc host struct
867f8968665STyrel Datwyler  * @queue:	ibmvfc queue struct
868f8968665STyrel Datwyler  *
869f8968665STyrel Datwyler  * Unmaps dma and deallocates page for messages
870f8968665STyrel Datwyler  **/
ibmvfc_free_queue(struct ibmvfc_host * vhost,struct ibmvfc_queue * queue)871f8968665STyrel Datwyler static void ibmvfc_free_queue(struct ibmvfc_host *vhost,
872f8968665STyrel Datwyler 			      struct ibmvfc_queue *queue)
873f8968665STyrel Datwyler {
874f8968665STyrel Datwyler 	struct device *dev = vhost->dev;
875f8968665STyrel Datwyler 
876f8968665STyrel Datwyler 	dma_unmap_single(dev, queue->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
877f8968665STyrel Datwyler 	free_page((unsigned long)queue->msgs.handle);
878f8968665STyrel Datwyler 	queue->msgs.handle = NULL;
879003d91a1STyrel Datwyler 
880003d91a1STyrel Datwyler 	ibmvfc_free_event_pool(vhost, queue);
881f8968665STyrel Datwyler }
882f8968665STyrel Datwyler 
883f8968665STyrel Datwyler /**
884072b91f9SBrian King  * ibmvfc_release_crq_queue - Deallocates data and unregisters CRQ
885072b91f9SBrian King  * @vhost:	ibmvfc host struct
886072b91f9SBrian King  *
887072b91f9SBrian King  * Frees irq, deallocates a page for messages, unmaps dma, and unregisters
888072b91f9SBrian King  * the crq with the hypervisor.
889072b91f9SBrian King  **/
ibmvfc_release_crq_queue(struct ibmvfc_host * vhost)890072b91f9SBrian King static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
891072b91f9SBrian King {
89273ee5d86SBrian King 	long rc = 0;
893072b91f9SBrian King 	struct vio_dev *vdev = to_vio_dev(vhost->dev);
894f8968665STyrel Datwyler 	struct ibmvfc_queue *crq = &vhost->crq;
895072b91f9SBrian King 
896072b91f9SBrian King 	ibmvfc_dbg(vhost, "Releasing CRQ\n");
897072b91f9SBrian King 	free_irq(vdev->irq, vhost);
898039a0898SBrian King 	tasklet_kill(&vhost->tasklet);
899072b91f9SBrian King 	do {
90073ee5d86SBrian King 		if (rc)
90173ee5d86SBrian King 			msleep(100);
902072b91f9SBrian King 		rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
903072b91f9SBrian King 	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
904072b91f9SBrian King 
905072b91f9SBrian King 	vhost->state = IBMVFC_NO_CRQ;
90679111d08SBrian King 	vhost->logged_in = 0;
907f8968665STyrel Datwyler 
908f8968665STyrel Datwyler 	ibmvfc_free_queue(vhost, crq);
909072b91f9SBrian King }
910072b91f9SBrian King 
911072b91f9SBrian King /**
912072b91f9SBrian King  * ibmvfc_reenable_crq_queue - reenables the CRQ
913072b91f9SBrian King  * @vhost:	ibmvfc host struct
914072b91f9SBrian King  *
915072b91f9SBrian King  * Return value:
916072b91f9SBrian King  *	0 on success / other on failure
917072b91f9SBrian King  **/
ibmvfc_reenable_crq_queue(struct ibmvfc_host * vhost)918072b91f9SBrian King static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
919072b91f9SBrian King {
92073ee5d86SBrian King 	int rc = 0;
921072b91f9SBrian King 	struct vio_dev *vdev = to_vio_dev(vhost->dev);
922dbdbb81bSTyrel Datwyler 	unsigned long flags;
923dbdbb81bSTyrel Datwyler 
92472ea7fe0STyrel Datwyler 	ibmvfc_dereg_sub_crqs(vhost);
925072b91f9SBrian King 
926072b91f9SBrian King 	/* Re-enable the CRQ */
927072b91f9SBrian King 	do {
92873ee5d86SBrian King 		if (rc)
92973ee5d86SBrian King 			msleep(100);
930072b91f9SBrian King 		rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address);
931072b91f9SBrian King 	} while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
932072b91f9SBrian King 
933072b91f9SBrian King 	if (rc)
934072b91f9SBrian King 		dev_err(vhost->dev, "Error enabling adapter (rc=%d)\n", rc);
935072b91f9SBrian King 
936dbdbb81bSTyrel Datwyler 	spin_lock_irqsave(vhost->host->host_lock, flags);
937dbdbb81bSTyrel Datwyler 	spin_lock(vhost->crq.q_lock);
938dbdbb81bSTyrel Datwyler 	vhost->do_enquiry = 1;
939dbdbb81bSTyrel Datwyler 	vhost->using_channels = 0;
940dbdbb81bSTyrel Datwyler 	spin_unlock(vhost->crq.q_lock);
941dbdbb81bSTyrel Datwyler 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
942dbdbb81bSTyrel Datwyler 
94372ea7fe0STyrel Datwyler 	ibmvfc_reg_sub_crqs(vhost);
944dbdbb81bSTyrel Datwyler 
945072b91f9SBrian King 	return rc;
946072b91f9SBrian King }
947072b91f9SBrian King 
948072b91f9SBrian King /**
949072b91f9SBrian King  * ibmvfc_reset_crq - resets a crq after a failure
950072b91f9SBrian King  * @vhost:	ibmvfc host struct
951072b91f9SBrian King  *
952072b91f9SBrian King  * Return value:
953072b91f9SBrian King  *	0 on success / other on failure
954072b91f9SBrian King  **/
ibmvfc_reset_crq(struct ibmvfc_host * vhost)955072b91f9SBrian King static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
956072b91f9SBrian King {
95773ee5d86SBrian King 	int rc = 0;
95873ee5d86SBrian King 	unsigned long flags;
959072b91f9SBrian King 	struct vio_dev *vdev = to_vio_dev(vhost->dev);
960f8968665STyrel Datwyler 	struct ibmvfc_queue *crq = &vhost->crq;
9615cf52964STyrel Datwyler 
96272ea7fe0STyrel Datwyler 	ibmvfc_dereg_sub_crqs(vhost);
963072b91f9SBrian King 
964072b91f9SBrian King 	/* Close the CRQ */
965072b91f9SBrian King 	do {
96673ee5d86SBrian King 		if (rc)
96773ee5d86SBrian King 			msleep(100);
968072b91f9SBrian King 		rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
969072b91f9SBrian King 	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
970072b91f9SBrian King 
97173ee5d86SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
97257e80e0bSTyrel Datwyler 	spin_lock(vhost->crq.q_lock);
973072b91f9SBrian King 	vhost->state = IBMVFC_NO_CRQ;
97479111d08SBrian King 	vhost->logged_in = 0;
975e95eef3fSTyrel Datwyler 	vhost->do_enquiry = 1;
976e95eef3fSTyrel Datwyler 	vhost->using_channels = 0;
977072b91f9SBrian King 
978072b91f9SBrian King 	/* Clean out the queue */
979f8968665STyrel Datwyler 	memset(crq->msgs.crq, 0, PAGE_SIZE);
980072b91f9SBrian King 	crq->cur = 0;
981072b91f9SBrian King 
982072b91f9SBrian King 	/* And re-open it again */
983072b91f9SBrian King 	rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
984072b91f9SBrian King 				crq->msg_token, PAGE_SIZE);
985072b91f9SBrian King 
986072b91f9SBrian King 	if (rc == H_CLOSED)
987072b91f9SBrian King 		/* Adapter is good, but other end is not ready */
988072b91f9SBrian King 		dev_warn(vhost->dev, "Partner adapter not ready\n");
989072b91f9SBrian King 	else if (rc != 0)
990072b91f9SBrian King 		dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc);
9915cf52964STyrel Datwyler 
99257e80e0bSTyrel Datwyler 	spin_unlock(vhost->crq.q_lock);
99373ee5d86SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
994072b91f9SBrian King 
99572ea7fe0STyrel Datwyler 	ibmvfc_reg_sub_crqs(vhost);
9965cf52964STyrel Datwyler 
997072b91f9SBrian King 	return rc;
998072b91f9SBrian King }
999072b91f9SBrian King 
1000072b91f9SBrian King /**
1001072b91f9SBrian King  * ibmvfc_valid_event - Determines if event is valid.
1002072b91f9SBrian King  * @pool:	event_pool that contains the event
1003072b91f9SBrian King  * @evt:	ibmvfc event to be checked for validity
1004072b91f9SBrian King  *
1005072b91f9SBrian King  * Return value:
1006072b91f9SBrian King  *	1 if event is valid / 0 if event is not valid
1007072b91f9SBrian King  **/
ibmvfc_valid_event(struct ibmvfc_event_pool * pool,struct ibmvfc_event * evt)1008072b91f9SBrian King static int ibmvfc_valid_event(struct ibmvfc_event_pool *pool,
1009072b91f9SBrian King 			      struct ibmvfc_event *evt)
1010072b91f9SBrian King {
1011072b91f9SBrian King 	int index = evt - pool->events;
1012072b91f9SBrian King 	if (index < 0 || index >= pool->size)	/* outside of bounds */
1013072b91f9SBrian King 		return 0;
1014072b91f9SBrian King 	if (evt != pool->events + index)	/* unaligned */
1015072b91f9SBrian King 		return 0;
1016072b91f9SBrian King 	return 1;
1017072b91f9SBrian King }
1018072b91f9SBrian King 
1019072b91f9SBrian King /**
1020072b91f9SBrian King  * ibmvfc_free_event - Free the specified event
1021072b91f9SBrian King  * @evt:	ibmvfc_event to be freed
1022072b91f9SBrian King  *
1023072b91f9SBrian King  **/
ibmvfc_free_event(struct ibmvfc_event * evt)1024072b91f9SBrian King static void ibmvfc_free_event(struct ibmvfc_event *evt)
1025072b91f9SBrian King {
1026e4b26f3dSTyrel Datwyler 	struct ibmvfc_event_pool *pool = &evt->queue->evt_pool;
102757e80e0bSTyrel Datwyler 	unsigned long flags;
1028072b91f9SBrian King 
1029072b91f9SBrian King 	BUG_ON(!ibmvfc_valid_event(pool, evt));
1030072b91f9SBrian King 	BUG_ON(atomic_inc_return(&evt->free) != 1);
1031a264cf5eSTyrel Datwyler 	BUG_ON(atomic_dec_and_test(&evt->active));
103257e80e0bSTyrel Datwyler 
103357e80e0bSTyrel Datwyler 	spin_lock_irqsave(&evt->queue->l_lock, flags);
1034e4b26f3dSTyrel Datwyler 	list_add_tail(&evt->queue_list, &evt->queue->free);
103557e80e0bSTyrel Datwyler 	if (evt->eh_comp)
103657e80e0bSTyrel Datwyler 		complete(evt->eh_comp);
103757e80e0bSTyrel Datwyler 	spin_unlock_irqrestore(&evt->queue->l_lock, flags);
1038072b91f9SBrian King }
1039072b91f9SBrian King 
1040072b91f9SBrian King /**
1041072b91f9SBrian King  * ibmvfc_scsi_eh_done - EH done function for queuecommand commands
1042072b91f9SBrian King  * @evt:	ibmvfc event struct
1043072b91f9SBrian King  *
1044072b91f9SBrian King  * This function does not setup any error status, that must be done
1045072b91f9SBrian King  * before this function gets called.
1046072b91f9SBrian King  **/
ibmvfc_scsi_eh_done(struct ibmvfc_event * evt)1047072b91f9SBrian King static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
1048072b91f9SBrian King {
1049072b91f9SBrian King 	struct scsi_cmnd *cmnd = evt->cmnd;
1050072b91f9SBrian King 
1051072b91f9SBrian King 	if (cmnd) {
1052072b91f9SBrian King 		scsi_dma_unmap(cmnd);
105385f6dd08SBart Van Assche 		scsi_done(cmnd);
1054072b91f9SBrian King 	}
1055072b91f9SBrian King 
1056072b91f9SBrian King 	ibmvfc_free_event(evt);
1057072b91f9SBrian King }
1058072b91f9SBrian King 
1059072b91f9SBrian King /**
106057e80e0bSTyrel Datwyler  * ibmvfc_complete_purge - Complete failed command list
106157e80e0bSTyrel Datwyler  * @purge_list:		list head of failed commands
106257e80e0bSTyrel Datwyler  *
106357e80e0bSTyrel Datwyler  * This function runs completions on commands to fail as a result of a
10641f4a4a19STyrel Datwyler  * host reset or platform migration.
106557e80e0bSTyrel Datwyler  **/
ibmvfc_complete_purge(struct list_head * purge_list)106657e80e0bSTyrel Datwyler static void ibmvfc_complete_purge(struct list_head *purge_list)
106757e80e0bSTyrel Datwyler {
106857e80e0bSTyrel Datwyler 	struct ibmvfc_event *evt, *pos;
106957e80e0bSTyrel Datwyler 
107057e80e0bSTyrel Datwyler 	list_for_each_entry_safe(evt, pos, purge_list, queue_list) {
107157e80e0bSTyrel Datwyler 		list_del(&evt->queue_list);
107257e80e0bSTyrel Datwyler 		ibmvfc_trc_end(evt);
107357e80e0bSTyrel Datwyler 		evt->done(evt);
107457e80e0bSTyrel Datwyler 	}
107557e80e0bSTyrel Datwyler }
107657e80e0bSTyrel Datwyler 
107757e80e0bSTyrel Datwyler /**
1078072b91f9SBrian King  * ibmvfc_fail_request - Fail request with specified error code
1079072b91f9SBrian King  * @evt:		ibmvfc event struct
1080072b91f9SBrian King  * @error_code:	error code to fail request with
1081072b91f9SBrian King  *
1082072b91f9SBrian King  * Return value:
1083072b91f9SBrian King  *	none
1084072b91f9SBrian King  **/
ibmvfc_fail_request(struct ibmvfc_event * evt,int error_code)1085072b91f9SBrian King static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
1086072b91f9SBrian King {
1087a264cf5eSTyrel Datwyler 	/*
1088a264cf5eSTyrel Datwyler 	 * Anything we are failing should still be active. Otherwise, it
1089a264cf5eSTyrel Datwyler 	 * implies we already got a response for the command and are doing
1090a264cf5eSTyrel Datwyler 	 * something bad like double completing it.
1091a264cf5eSTyrel Datwyler 	 */
1092a264cf5eSTyrel Datwyler 	BUG_ON(!atomic_dec_and_test(&evt->active));
1093072b91f9SBrian King 	if (evt->cmnd) {
1094072b91f9SBrian King 		evt->cmnd->result = (error_code << 16);
1095072b91f9SBrian King 		evt->done = ibmvfc_scsi_eh_done;
1096072b91f9SBrian King 	} else
10970aab6c3fSTyrel Datwyler 		evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_DRIVER_FAILED);
1098072b91f9SBrian King 
1099072b91f9SBrian King 	del_timer(&evt->timer);
1100072b91f9SBrian King }
1101072b91f9SBrian King 
1102072b91f9SBrian King /**
1103072b91f9SBrian King  * ibmvfc_purge_requests - Our virtual adapter just shut down. Purge any sent requests
1104072b91f9SBrian King  * @vhost:		ibmvfc host struct
1105072b91f9SBrian King  * @error_code:	error code to fail requests with
1106072b91f9SBrian King  *
1107072b91f9SBrian King  * Return value:
1108072b91f9SBrian King  *	none
1109072b91f9SBrian King  **/
ibmvfc_purge_requests(struct ibmvfc_host * vhost,int error_code)1110072b91f9SBrian King static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
1111072b91f9SBrian King {
1112072b91f9SBrian King 	struct ibmvfc_event *evt, *pos;
11137eb3ccd8STyrel Datwyler 	struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs;
111457e80e0bSTyrel Datwyler 	unsigned long flags;
11157eb3ccd8STyrel Datwyler 	int hwqs = 0;
11167eb3ccd8STyrel Datwyler 	int i;
11177eb3ccd8STyrel Datwyler 
11187eb3ccd8STyrel Datwyler 	if (vhost->using_channels)
11197eb3ccd8STyrel Datwyler 		hwqs = vhost->scsi_scrqs.active_queues;
1120072b91f9SBrian King 
1121072b91f9SBrian King 	ibmvfc_dbg(vhost, "Purging all requests\n");
112257e80e0bSTyrel Datwyler 	spin_lock_irqsave(&vhost->crq.l_lock, flags);
1123e4b26f3dSTyrel Datwyler 	list_for_each_entry_safe(evt, pos, &vhost->crq.sent, queue_list)
1124072b91f9SBrian King 		ibmvfc_fail_request(evt, error_code);
112557e80e0bSTyrel Datwyler 	list_splice_init(&vhost->crq.sent, &vhost->purge);
112657e80e0bSTyrel Datwyler 	spin_unlock_irqrestore(&vhost->crq.l_lock, flags);
11277eb3ccd8STyrel Datwyler 
11287eb3ccd8STyrel Datwyler 	for (i = 0; i < hwqs; i++) {
11297eb3ccd8STyrel Datwyler 		spin_lock_irqsave(queues[i].q_lock, flags);
11307eb3ccd8STyrel Datwyler 		spin_lock(&queues[i].l_lock);
11317eb3ccd8STyrel Datwyler 		list_for_each_entry_safe(evt, pos, &queues[i].sent, queue_list)
11327eb3ccd8STyrel Datwyler 			ibmvfc_fail_request(evt, error_code);
11337eb3ccd8STyrel Datwyler 		list_splice_init(&queues[i].sent, &vhost->purge);
11347eb3ccd8STyrel Datwyler 		spin_unlock(&queues[i].l_lock);
11357eb3ccd8STyrel Datwyler 		spin_unlock_irqrestore(queues[i].q_lock, flags);
11367eb3ccd8STyrel Datwyler 	}
1137072b91f9SBrian King }
1138072b91f9SBrian King 
1139072b91f9SBrian King /**
114079111d08SBrian King  * ibmvfc_hard_reset_host - Reset the connection to the server by breaking the CRQ
1141072b91f9SBrian King  * @vhost:	struct ibmvfc host to reset
1142072b91f9SBrian King  **/
ibmvfc_hard_reset_host(struct ibmvfc_host * vhost)114379111d08SBrian King static void ibmvfc_hard_reset_host(struct ibmvfc_host *vhost)
1144072b91f9SBrian King {
1145072b91f9SBrian King 	ibmvfc_purge_requests(vhost, DID_ERROR);
1146072b91f9SBrian King 	ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
114773ee5d86SBrian King 	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET);
1148072b91f9SBrian King }
1149072b91f9SBrian King 
1150072b91f9SBrian King /**
115179111d08SBrian King  * __ibmvfc_reset_host - Reset the connection to the server (no locking)
1152072b91f9SBrian King  * @vhost:	struct ibmvfc host to reset
1153072b91f9SBrian King  **/
__ibmvfc_reset_host(struct ibmvfc_host * vhost)115479111d08SBrian King static void __ibmvfc_reset_host(struct ibmvfc_host *vhost)
115579111d08SBrian King {
115679111d08SBrian King 	if (vhost->logged_in && vhost->action != IBMVFC_HOST_ACTION_LOGO_WAIT &&
115779111d08SBrian King 	    !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
115879111d08SBrian King 		scsi_block_requests(vhost->host);
115979111d08SBrian King 		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO);
116079111d08SBrian King 		vhost->job_step = ibmvfc_npiv_logout;
116179111d08SBrian King 		wake_up(&vhost->work_wait_q);
116279111d08SBrian King 	} else
116379111d08SBrian King 		ibmvfc_hard_reset_host(vhost);
116479111d08SBrian King }
116579111d08SBrian King 
116679111d08SBrian King /**
116779111d08SBrian King  * ibmvfc_reset_host - Reset the connection to the server
116879111d08SBrian King  * @vhost:	ibmvfc host struct
116979111d08SBrian King  **/
ibmvfc_reset_host(struct ibmvfc_host * vhost)1170072b91f9SBrian King static void ibmvfc_reset_host(struct ibmvfc_host *vhost)
1171072b91f9SBrian King {
1172072b91f9SBrian King 	unsigned long flags;
1173072b91f9SBrian King 
1174072b91f9SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
1175072b91f9SBrian King 	__ibmvfc_reset_host(vhost);
1176072b91f9SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
1177072b91f9SBrian King }
1178072b91f9SBrian King 
1179072b91f9SBrian King /**
1180072b91f9SBrian King  * ibmvfc_retry_host_init - Retry host initialization if allowed
1181072b91f9SBrian King  * @vhost:	ibmvfc host struct
1182072b91f9SBrian King  *
11837d0e4622SBrian King  * Returns: 1 if init will be retried / 0 if not
11847d0e4622SBrian King  *
1185072b91f9SBrian King  **/
ibmvfc_retry_host_init(struct ibmvfc_host * vhost)11867d0e4622SBrian King static int ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
1187072b91f9SBrian King {
11887d0e4622SBrian King 	int retry = 0;
11897d0e4622SBrian King 
1190072b91f9SBrian King 	if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
11911c41fa82SBrian King 		vhost->delay_init = 1;
11921c41fa82SBrian King 		if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
1193072b91f9SBrian King 			dev_err(vhost->dev,
1194072b91f9SBrian King 				"Host initialization retries exceeded. Taking adapter offline\n");
1195072b91f9SBrian King 			ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
11961c41fa82SBrian King 		} else if (vhost->init_retries == IBMVFC_MAX_HOST_INIT_RETRIES)
1197072b91f9SBrian King 			__ibmvfc_reset_host(vhost);
11987d0e4622SBrian King 		else {
1199072b91f9SBrian King 			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
12007d0e4622SBrian King 			retry = 1;
12017d0e4622SBrian King 		}
1202072b91f9SBrian King 	}
1203072b91f9SBrian King 
1204072b91f9SBrian King 	wake_up(&vhost->work_wait_q);
12057d0e4622SBrian King 	return retry;
1206072b91f9SBrian King }
1207072b91f9SBrian King 
1208072b91f9SBrian King /**
1209b3c10489SBrian King  * __ibmvfc_get_target - Find the specified scsi_target (no locking)
1210072b91f9SBrian King  * @starget:	scsi target struct
1211072b91f9SBrian King  *
1212072b91f9SBrian King  * Return value:
1213072b91f9SBrian King  *	ibmvfc_target struct / NULL if not found
1214072b91f9SBrian King  **/
__ibmvfc_get_target(struct scsi_target * starget)1215b3c10489SBrian King static struct ibmvfc_target *__ibmvfc_get_target(struct scsi_target *starget)
1216072b91f9SBrian King {
1217072b91f9SBrian King 	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
1218072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
1219072b91f9SBrian King 	struct ibmvfc_target *tgt;
1220072b91f9SBrian King 
1221072b91f9SBrian King 	list_for_each_entry(tgt, &vhost->targets, queue)
1222b3c10489SBrian King 		if (tgt->target_id == starget->id) {
1223b3c10489SBrian King 			kref_get(&tgt->kref);
1224072b91f9SBrian King 			return tgt;
1225b3c10489SBrian King 		}
1226072b91f9SBrian King 	return NULL;
1227072b91f9SBrian King }
1228072b91f9SBrian King 
1229072b91f9SBrian King /**
1230b3c10489SBrian King  * ibmvfc_get_target - Find the specified scsi_target
1231072b91f9SBrian King  * @starget:	scsi target struct
1232072b91f9SBrian King  *
1233072b91f9SBrian King  * Return value:
1234072b91f9SBrian King  *	ibmvfc_target struct / NULL if not found
1235072b91f9SBrian King  **/
ibmvfc_get_target(struct scsi_target * starget)1236b3c10489SBrian King static struct ibmvfc_target *ibmvfc_get_target(struct scsi_target *starget)
1237072b91f9SBrian King {
1238072b91f9SBrian King 	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
1239072b91f9SBrian King 	struct ibmvfc_target *tgt;
1240072b91f9SBrian King 	unsigned long flags;
1241072b91f9SBrian King 
1242072b91f9SBrian King 	spin_lock_irqsave(shost->host_lock, flags);
1243b3c10489SBrian King 	tgt = __ibmvfc_get_target(starget);
1244072b91f9SBrian King 	spin_unlock_irqrestore(shost->host_lock, flags);
1245072b91f9SBrian King 	return tgt;
1246072b91f9SBrian King }
1247072b91f9SBrian King 
1248072b91f9SBrian King /**
1249072b91f9SBrian King  * ibmvfc_get_host_speed - Get host port speed
1250072b91f9SBrian King  * @shost:		scsi host struct
1251072b91f9SBrian King  *
1252072b91f9SBrian King  * Return value:
1253072b91f9SBrian King  * 	none
1254072b91f9SBrian King  **/
ibmvfc_get_host_speed(struct Scsi_Host * shost)1255072b91f9SBrian King static void ibmvfc_get_host_speed(struct Scsi_Host *shost)
1256072b91f9SBrian King {
1257072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
1258072b91f9SBrian King 	unsigned long flags;
1259072b91f9SBrian King 
1260072b91f9SBrian King 	spin_lock_irqsave(shost->host_lock, flags);
1261072b91f9SBrian King 	if (vhost->state == IBMVFC_ACTIVE) {
12620aab6c3fSTyrel Datwyler 		switch (be64_to_cpu(vhost->login_buf->resp.link_speed) / 100) {
1263072b91f9SBrian King 		case 1:
1264072b91f9SBrian King 			fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
1265072b91f9SBrian King 			break;
1266072b91f9SBrian King 		case 2:
1267072b91f9SBrian King 			fc_host_speed(shost) = FC_PORTSPEED_2GBIT;
1268072b91f9SBrian King 			break;
1269072b91f9SBrian King 		case 4:
1270072b91f9SBrian King 			fc_host_speed(shost) = FC_PORTSPEED_4GBIT;
1271072b91f9SBrian King 			break;
1272072b91f9SBrian King 		case 8:
1273072b91f9SBrian King 			fc_host_speed(shost) = FC_PORTSPEED_8GBIT;
1274072b91f9SBrian King 			break;
1275072b91f9SBrian King 		case 10:
1276072b91f9SBrian King 			fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
1277072b91f9SBrian King 			break;
1278072b91f9SBrian King 		case 16:
1279072b91f9SBrian King 			fc_host_speed(shost) = FC_PORTSPEED_16GBIT;
1280072b91f9SBrian King 			break;
1281072b91f9SBrian King 		default:
1282775a42ecSStephen Rothwell 			ibmvfc_log(vhost, 3, "Unknown port speed: %lld Gbit\n",
12830aab6c3fSTyrel Datwyler 				   be64_to_cpu(vhost->login_buf->resp.link_speed) / 100);
1284072b91f9SBrian King 			fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
1285072b91f9SBrian King 			break;
1286072b91f9SBrian King 		}
1287072b91f9SBrian King 	} else
1288072b91f9SBrian King 		fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
1289072b91f9SBrian King 	spin_unlock_irqrestore(shost->host_lock, flags);
1290072b91f9SBrian King }
1291072b91f9SBrian King 
1292072b91f9SBrian King /**
1293072b91f9SBrian King  * ibmvfc_get_host_port_state - Get host port state
1294072b91f9SBrian King  * @shost:		scsi host struct
1295072b91f9SBrian King  *
1296072b91f9SBrian King  * Return value:
1297072b91f9SBrian King  * 	none
1298072b91f9SBrian King  **/
ibmvfc_get_host_port_state(struct Scsi_Host * shost)1299072b91f9SBrian King static void ibmvfc_get_host_port_state(struct Scsi_Host *shost)
1300072b91f9SBrian King {
1301072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
1302072b91f9SBrian King 	unsigned long flags;
1303072b91f9SBrian King 
1304072b91f9SBrian King 	spin_lock_irqsave(shost->host_lock, flags);
1305072b91f9SBrian King 	switch (vhost->state) {
1306072b91f9SBrian King 	case IBMVFC_INITIALIZING:
1307072b91f9SBrian King 	case IBMVFC_ACTIVE:
1308072b91f9SBrian King 		fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
1309072b91f9SBrian King 		break;
1310072b91f9SBrian King 	case IBMVFC_LINK_DOWN:
1311072b91f9SBrian King 		fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
1312072b91f9SBrian King 		break;
1313072b91f9SBrian King 	case IBMVFC_LINK_DEAD:
1314072b91f9SBrian King 	case IBMVFC_HOST_OFFLINE:
1315072b91f9SBrian King 		fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
1316072b91f9SBrian King 		break;
1317072b91f9SBrian King 	case IBMVFC_HALTED:
1318072b91f9SBrian King 		fc_host_port_state(shost) = FC_PORTSTATE_BLOCKED;
1319072b91f9SBrian King 		break;
13200ae808e0SBrian King 	case IBMVFC_NO_CRQ:
13210ae808e0SBrian King 		fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
13220ae808e0SBrian King 		break;
1323072b91f9SBrian King 	default:
1324072b91f9SBrian King 		ibmvfc_log(vhost, 3, "Unknown port state: %d\n", vhost->state);
1325072b91f9SBrian King 		fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
1326072b91f9SBrian King 		break;
1327072b91f9SBrian King 	}
1328072b91f9SBrian King 	spin_unlock_irqrestore(shost->host_lock, flags);
1329072b91f9SBrian King }
1330072b91f9SBrian King 
1331072b91f9SBrian King /**
1332072b91f9SBrian King  * ibmvfc_set_rport_dev_loss_tmo - Set rport's device loss timeout
1333072b91f9SBrian King  * @rport:		rport struct
1334072b91f9SBrian King  * @timeout:	timeout value
1335072b91f9SBrian King  *
1336072b91f9SBrian King  * Return value:
1337072b91f9SBrian King  * 	none
1338072b91f9SBrian King  **/
ibmvfc_set_rport_dev_loss_tmo(struct fc_rport * rport,u32 timeout)1339072b91f9SBrian King static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
1340072b91f9SBrian King {
1341072b91f9SBrian King 	if (timeout)
1342072b91f9SBrian King 		rport->dev_loss_tmo = timeout;
1343072b91f9SBrian King 	else
1344072b91f9SBrian King 		rport->dev_loss_tmo = 1;
1345072b91f9SBrian King }
1346072b91f9SBrian King 
1347072b91f9SBrian King /**
1348b3c10489SBrian King  * ibmvfc_release_tgt - Free memory allocated for a target
1349b3c10489SBrian King  * @kref:		kref struct
1350b3c10489SBrian King  *
1351b3c10489SBrian King  **/
ibmvfc_release_tgt(struct kref * kref)1352b3c10489SBrian King static void ibmvfc_release_tgt(struct kref *kref)
1353b3c10489SBrian King {
1354b3c10489SBrian King 	struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
1355b3c10489SBrian King 	kfree(tgt);
1356b3c10489SBrian King }
1357b3c10489SBrian King 
1358b3c10489SBrian King /**
1359072b91f9SBrian King  * ibmvfc_get_starget_node_name - Get SCSI target's node name
1360072b91f9SBrian King  * @starget:	scsi target struct
1361072b91f9SBrian King  *
1362072b91f9SBrian King  * Return value:
1363072b91f9SBrian King  * 	none
1364072b91f9SBrian King  **/
ibmvfc_get_starget_node_name(struct scsi_target * starget)1365072b91f9SBrian King static void ibmvfc_get_starget_node_name(struct scsi_target *starget)
1366072b91f9SBrian King {
1367b3c10489SBrian King 	struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
1368072b91f9SBrian King 	fc_starget_port_name(starget) = tgt ? tgt->ids.node_name : 0;
1369b3c10489SBrian King 	if (tgt)
1370b3c10489SBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
1371072b91f9SBrian King }
1372072b91f9SBrian King 
1373072b91f9SBrian King /**
1374072b91f9SBrian King  * ibmvfc_get_starget_port_name - Get SCSI target's port name
1375072b91f9SBrian King  * @starget:	scsi target struct
1376072b91f9SBrian King  *
1377072b91f9SBrian King  * Return value:
1378072b91f9SBrian King  * 	none
1379072b91f9SBrian King  **/
ibmvfc_get_starget_port_name(struct scsi_target * starget)1380072b91f9SBrian King static void ibmvfc_get_starget_port_name(struct scsi_target *starget)
1381072b91f9SBrian King {
1382b3c10489SBrian King 	struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
1383072b91f9SBrian King 	fc_starget_port_name(starget) = tgt ? tgt->ids.port_name : 0;
1384b3c10489SBrian King 	if (tgt)
1385b3c10489SBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
1386072b91f9SBrian King }
1387072b91f9SBrian King 
1388072b91f9SBrian King /**
1389072b91f9SBrian King  * ibmvfc_get_starget_port_id - Get SCSI target's port ID
1390072b91f9SBrian King  * @starget:	scsi target struct
1391072b91f9SBrian King  *
1392072b91f9SBrian King  * Return value:
1393072b91f9SBrian King  * 	none
1394072b91f9SBrian King  **/
ibmvfc_get_starget_port_id(struct scsi_target * starget)1395072b91f9SBrian King static void ibmvfc_get_starget_port_id(struct scsi_target *starget)
1396072b91f9SBrian King {
1397b3c10489SBrian King 	struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
1398072b91f9SBrian King 	fc_starget_port_id(starget) = tgt ? tgt->scsi_id : -1;
1399b3c10489SBrian King 	if (tgt)
1400b3c10489SBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
1401072b91f9SBrian King }
1402072b91f9SBrian King 
1403072b91f9SBrian King /**
1404072b91f9SBrian King  * ibmvfc_wait_while_resetting - Wait while the host resets
1405072b91f9SBrian King  * @vhost:		ibmvfc host struct
1406072b91f9SBrian King  *
1407072b91f9SBrian King  * Return value:
1408072b91f9SBrian King  * 	0 on success / other on failure
1409072b91f9SBrian King  **/
ibmvfc_wait_while_resetting(struct ibmvfc_host * vhost)1410072b91f9SBrian King static int ibmvfc_wait_while_resetting(struct ibmvfc_host *vhost)
1411072b91f9SBrian King {
1412072b91f9SBrian King 	long timeout = wait_event_timeout(vhost->init_wait_q,
14133eddc569SBrian King 					  ((vhost->state == IBMVFC_ACTIVE ||
1414072b91f9SBrian King 					    vhost->state == IBMVFC_HOST_OFFLINE ||
14153eddc569SBrian King 					    vhost->state == IBMVFC_LINK_DEAD) &&
14163eddc569SBrian King 					   vhost->action == IBMVFC_HOST_ACTION_NONE),
1417072b91f9SBrian King 					  (init_timeout * HZ));
1418072b91f9SBrian King 
1419072b91f9SBrian King 	return timeout ? 0 : -EIO;
1420072b91f9SBrian King }
1421072b91f9SBrian King 
1422072b91f9SBrian King /**
1423072b91f9SBrian King  * ibmvfc_issue_fc_host_lip - Re-initiate link initialization
1424072b91f9SBrian King  * @shost:		scsi host struct
1425072b91f9SBrian King  *
1426072b91f9SBrian King  * Return value:
1427072b91f9SBrian King  * 	0 on success / other on failure
1428072b91f9SBrian King  **/
ibmvfc_issue_fc_host_lip(struct Scsi_Host * shost)1429072b91f9SBrian King static int ibmvfc_issue_fc_host_lip(struct Scsi_Host *shost)
1430072b91f9SBrian King {
1431072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
1432072b91f9SBrian King 
1433072b91f9SBrian King 	dev_err(vhost->dev, "Initiating host LIP. Resetting connection\n");
1434072b91f9SBrian King 	ibmvfc_reset_host(vhost);
1435072b91f9SBrian King 	return ibmvfc_wait_while_resetting(vhost);
1436072b91f9SBrian King }
1437072b91f9SBrian King 
1438072b91f9SBrian King /**
1439072b91f9SBrian King  * ibmvfc_gather_partition_info - Gather info about the LPAR
1440dd9c7729SLee Jones  * @vhost:      ibmvfc host struct
1441072b91f9SBrian King  *
1442072b91f9SBrian King  * Return value:
1443072b91f9SBrian King  *	none
1444072b91f9SBrian King  **/
ibmvfc_gather_partition_info(struct ibmvfc_host * vhost)1445072b91f9SBrian King static void ibmvfc_gather_partition_info(struct ibmvfc_host *vhost)
1446072b91f9SBrian King {
1447072b91f9SBrian King 	struct device_node *rootdn;
1448072b91f9SBrian King 	const char *name;
1449072b91f9SBrian King 	const unsigned int *num;
1450072b91f9SBrian King 
1451072b91f9SBrian King 	rootdn = of_find_node_by_path("/");
1452072b91f9SBrian King 	if (!rootdn)
1453072b91f9SBrian King 		return;
1454072b91f9SBrian King 
1455072b91f9SBrian King 	name = of_get_property(rootdn, "ibm,partition-name", NULL);
1456072b91f9SBrian King 	if (name)
1457072b91f9SBrian King 		strncpy(vhost->partition_name, name, sizeof(vhost->partition_name));
1458072b91f9SBrian King 	num = of_get_property(rootdn, "ibm,partition-no", NULL);
1459072b91f9SBrian King 	if (num)
1460072b91f9SBrian King 		vhost->partition_number = *num;
1461072b91f9SBrian King 	of_node_put(rootdn);
1462072b91f9SBrian King }
1463072b91f9SBrian King 
1464072b91f9SBrian King /**
1465072b91f9SBrian King  * ibmvfc_set_login_info - Setup info for NPIV login
1466072b91f9SBrian King  * @vhost:	ibmvfc host struct
1467072b91f9SBrian King  *
1468072b91f9SBrian King  * Return value:
1469072b91f9SBrian King  *	none
1470072b91f9SBrian King  **/
ibmvfc_set_login_info(struct ibmvfc_host * vhost)1471072b91f9SBrian King static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
1472072b91f9SBrian King {
1473072b91f9SBrian King 	struct ibmvfc_npiv_login *login_info = &vhost->login_info;
1474f8968665STyrel Datwyler 	struct ibmvfc_queue *async_crq = &vhost->async_crq;
147561c7a080SGrant Likely 	struct device_node *of_node = vhost->dev->of_node;
1476072b91f9SBrian King 	const char *location;
1477072b91f9SBrian King 
1478072b91f9SBrian King 	memset(login_info, 0, sizeof(*login_info));
1479072b91f9SBrian King 
14800aab6c3fSTyrel Datwyler 	login_info->ostype = cpu_to_be32(IBMVFC_OS_LINUX);
14810aab6c3fSTyrel Datwyler 	login_info->max_dma_len = cpu_to_be64(IBMVFC_MAX_SECTORS << 9);
14820aab6c3fSTyrel Datwyler 	login_info->max_payload = cpu_to_be32(sizeof(struct ibmvfc_fcp_cmd_iu));
14830aab6c3fSTyrel Datwyler 	login_info->max_response = cpu_to_be32(sizeof(struct ibmvfc_fcp_rsp));
14840aab6c3fSTyrel Datwyler 	login_info->partition_num = cpu_to_be32(vhost->partition_number);
14850aab6c3fSTyrel Datwyler 	login_info->vfc_frame_version = cpu_to_be32(1);
14860aab6c3fSTyrel Datwyler 	login_info->fcp_version = cpu_to_be16(3);
14870aab6c3fSTyrel Datwyler 	login_info->flags = cpu_to_be16(IBMVFC_FLUSH_ON_HALT);
1488072b91f9SBrian King 	if (vhost->client_migrated)
14890aab6c3fSTyrel Datwyler 		login_info->flags |= cpu_to_be16(IBMVFC_CLIENT_MIGRATED);
1490072b91f9SBrian King 
14910aab6c3fSTyrel Datwyler 	login_info->max_cmds = cpu_to_be32(max_requests + IBMVFC_NUM_INTERNAL_REQ);
1492e4af87b7STyrel Datwyler 	login_info->capabilities = cpu_to_be64(IBMVFC_CAN_MIGRATE | IBMVFC_CAN_SEND_VF_WWPN);
1493c53408baSTyrel Datwyler 
1494c53408baSTyrel Datwyler 	if (vhost->mq_enabled || vhost->using_channels)
1495c53408baSTyrel Datwyler 		login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS);
1496c53408baSTyrel Datwyler 
14970aab6c3fSTyrel Datwyler 	login_info->async.va = cpu_to_be64(vhost->async_crq.msg_token);
1498f8968665STyrel Datwyler 	login_info->async.len = cpu_to_be32(async_crq->size *
1499f8968665STyrel Datwyler 					    sizeof(*async_crq->msgs.async));
1500072b91f9SBrian King 	strncpy(login_info->partition_name, vhost->partition_name, IBMVFC_MAX_NAME);
1501072b91f9SBrian King 	strncpy(login_info->device_name,
150271610f55SKay Sievers 		dev_name(&vhost->host->shost_gendev), IBMVFC_MAX_NAME);
1503072b91f9SBrian King 
1504072b91f9SBrian King 	location = of_get_property(of_node, "ibm,loc-code", NULL);
150571610f55SKay Sievers 	location = location ? location : dev_name(vhost->dev);
1506072b91f9SBrian King 	strncpy(login_info->drc_name, location, IBMVFC_MAX_NAME);
1507072b91f9SBrian King }
1508072b91f9SBrian King 
1509072b91f9SBrian King /**
1510072b91f9SBrian King  * ibmvfc_get_event - Gets the next free event in pool
1511dd9c7729SLee Jones  * @queue:      ibmvfc queue struct
1512072b91f9SBrian King  *
1513072b91f9SBrian King  * Returns a free event from the pool.
1514072b91f9SBrian King  **/
ibmvfc_get_event(struct ibmvfc_queue * queue)1515e4b26f3dSTyrel Datwyler static struct ibmvfc_event *ibmvfc_get_event(struct ibmvfc_queue *queue)
1516072b91f9SBrian King {
1517072b91f9SBrian King 	struct ibmvfc_event *evt;
151857e80e0bSTyrel Datwyler 	unsigned long flags;
1519072b91f9SBrian King 
152057e80e0bSTyrel Datwyler 	spin_lock_irqsave(&queue->l_lock, flags);
1521*8bbe784cSTyrel Datwyler 	if (list_empty(&queue->free)) {
1522*8bbe784cSTyrel Datwyler 		ibmvfc_log(queue->vhost, 4, "empty event pool on queue:%ld\n", queue->hwq_id);
1523*8bbe784cSTyrel Datwyler 		spin_unlock_irqrestore(&queue->l_lock, flags);
1524*8bbe784cSTyrel Datwyler 		return NULL;
1525*8bbe784cSTyrel Datwyler 	}
1526e4b26f3dSTyrel Datwyler 	evt = list_entry(queue->free.next, struct ibmvfc_event, queue_list);
1527072b91f9SBrian King 	atomic_set(&evt->free, 0);
1528e4b26f3dSTyrel Datwyler 	list_del(&evt->queue_list);
152957e80e0bSTyrel Datwyler 	spin_unlock_irqrestore(&queue->l_lock, flags);
1530072b91f9SBrian King 	return evt;
1531072b91f9SBrian King }
1532072b91f9SBrian King 
1533072b91f9SBrian King /**
15341f4a4a19STyrel Datwyler  * ibmvfc_locked_done - Calls evt completion with host_lock held
15351f4a4a19STyrel Datwyler  * @evt:	ibmvfc evt to complete
15361f4a4a19STyrel Datwyler  *
15371f4a4a19STyrel Datwyler  * All non-scsi command completion callbacks have the expectation that the
15381f4a4a19STyrel Datwyler  * host_lock is held. This callback is used by ibmvfc_init_event to wrap a
15391f4a4a19STyrel Datwyler  * MAD evt with the host_lock.
15401f4a4a19STyrel Datwyler  **/
ibmvfc_locked_done(struct ibmvfc_event * evt)15411f4a4a19STyrel Datwyler static void ibmvfc_locked_done(struct ibmvfc_event *evt)
15421f4a4a19STyrel Datwyler {
15431f4a4a19STyrel Datwyler 	unsigned long flags;
15441f4a4a19STyrel Datwyler 
15451f4a4a19STyrel Datwyler 	spin_lock_irqsave(evt->vhost->host->host_lock, flags);
15461f4a4a19STyrel Datwyler 	evt->_done(evt);
15471f4a4a19STyrel Datwyler 	spin_unlock_irqrestore(evt->vhost->host->host_lock, flags);
15481f4a4a19STyrel Datwyler }
15491f4a4a19STyrel Datwyler 
15501f4a4a19STyrel Datwyler /**
1551072b91f9SBrian King  * ibmvfc_init_event - Initialize fields in an event struct that are always
1552072b91f9SBrian King  *				required.
1553072b91f9SBrian King  * @evt:	The event
1554072b91f9SBrian King  * @done:	Routine to call when the event is responded to
1555072b91f9SBrian King  * @format:	SRP or MAD format
1556072b91f9SBrian King  **/
ibmvfc_init_event(struct ibmvfc_event * evt,void (* done)(struct ibmvfc_event *),u8 format)1557072b91f9SBrian King static void ibmvfc_init_event(struct ibmvfc_event *evt,
1558072b91f9SBrian King 			      void (*done) (struct ibmvfc_event *), u8 format)
1559072b91f9SBrian King {
1560072b91f9SBrian King 	evt->cmnd = NULL;
1561072b91f9SBrian King 	evt->sync_iu = NULL;
1562ad8dcffaSBrian King 	evt->eh_comp = NULL;
15631f4a4a19STyrel Datwyler 	evt->crq.format = format;
15641f4a4a19STyrel Datwyler 	if (format == IBMVFC_CMD_FORMAT)
15651f4a4a19STyrel Datwyler 		evt->done = done;
15661f4a4a19STyrel Datwyler 	else {
15671f4a4a19STyrel Datwyler 		evt->_done = done;
15681f4a4a19STyrel Datwyler 		evt->done = ibmvfc_locked_done;
15691f4a4a19STyrel Datwyler 	}
1570cb72477bSTyrel Datwyler 	evt->hwq = 0;
1571072b91f9SBrian King }
1572072b91f9SBrian King 
1573072b91f9SBrian King /**
1574072b91f9SBrian King  * ibmvfc_map_sg_list - Initialize scatterlist
1575072b91f9SBrian King  * @scmd:	scsi command struct
1576072b91f9SBrian King  * @nseg:	number of scatterlist segments
1577072b91f9SBrian King  * @md:	memory descriptor list to initialize
1578072b91f9SBrian King  **/
ibmvfc_map_sg_list(struct scsi_cmnd * scmd,int nseg,struct srp_direct_buf * md)1579072b91f9SBrian King static void ibmvfc_map_sg_list(struct scsi_cmnd *scmd, int nseg,
1580072b91f9SBrian King 			       struct srp_direct_buf *md)
1581072b91f9SBrian King {
1582072b91f9SBrian King 	int i;
1583072b91f9SBrian King 	struct scatterlist *sg;
1584072b91f9SBrian King 
1585072b91f9SBrian King 	scsi_for_each_sg(scmd, sg, nseg, i) {
15860aab6c3fSTyrel Datwyler 		md[i].va = cpu_to_be64(sg_dma_address(sg));
15870aab6c3fSTyrel Datwyler 		md[i].len = cpu_to_be32(sg_dma_len(sg));
1588072b91f9SBrian King 		md[i].key = 0;
1589072b91f9SBrian King 	}
1590072b91f9SBrian King }
1591072b91f9SBrian King 
1592072b91f9SBrian King /**
15930a19a725SKieran Bingham  * ibmvfc_map_sg_data - Maps dma for a scatterlist and initializes descriptor fields
159491ebc1faSJohannes Thumshirn  * @scmd:		struct scsi_cmnd with the scatterlist
1595072b91f9SBrian King  * @evt:		ibmvfc event struct
1596072b91f9SBrian King  * @vfc_cmd:	vfc_cmd that contains the memory descriptor
1597072b91f9SBrian King  * @dev:		device for which to map dma memory
1598072b91f9SBrian King  *
1599072b91f9SBrian King  * Returns:
1600072b91f9SBrian King  *	0 on success / non-zero on failure
1601072b91f9SBrian King  **/
ibmvfc_map_sg_data(struct scsi_cmnd * scmd,struct ibmvfc_event * evt,struct ibmvfc_cmd * vfc_cmd,struct device * dev)1602072b91f9SBrian King static int ibmvfc_map_sg_data(struct scsi_cmnd *scmd,
1603072b91f9SBrian King 			      struct ibmvfc_event *evt,
1604072b91f9SBrian King 			      struct ibmvfc_cmd *vfc_cmd, struct device *dev)
1605072b91f9SBrian King {
1606072b91f9SBrian King 
1607072b91f9SBrian King 	int sg_mapped;
1608072b91f9SBrian King 	struct srp_direct_buf *data = &vfc_cmd->ioba;
1609072b91f9SBrian King 	struct ibmvfc_host *vhost = dev_get_drvdata(dev);
16105a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_cmd_iu *iu = ibmvfc_get_fcp_iu(evt->vhost, vfc_cmd);
1611072b91f9SBrian King 
1612a6104b1eSTyrel Datwyler 	if (cls3_error)
1613a6104b1eSTyrel Datwyler 		vfc_cmd->flags |= cpu_to_be16(IBMVFC_CLASS_3_ERR);
1614a6104b1eSTyrel Datwyler 
1615072b91f9SBrian King 	sg_mapped = scsi_dma_map(scmd);
1616072b91f9SBrian King 	if (!sg_mapped) {
16170aab6c3fSTyrel Datwyler 		vfc_cmd->flags |= cpu_to_be16(IBMVFC_NO_MEM_DESC);
1618072b91f9SBrian King 		return 0;
1619072b91f9SBrian King 	} else if (unlikely(sg_mapped < 0)) {
1620072b91f9SBrian King 		if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
1621072b91f9SBrian King 			scmd_printk(KERN_ERR, scmd, "Failed to map DMA buffer for command\n");
1622072b91f9SBrian King 		return sg_mapped;
1623072b91f9SBrian King 	}
1624072b91f9SBrian King 
1625072b91f9SBrian King 	if (scmd->sc_data_direction == DMA_TO_DEVICE) {
16260aab6c3fSTyrel Datwyler 		vfc_cmd->flags |= cpu_to_be16(IBMVFC_WRITE);
1627c16b8a6dSTyrel Datwyler 		iu->add_cdb_len |= IBMVFC_WRDATA;
1628072b91f9SBrian King 	} else {
16290aab6c3fSTyrel Datwyler 		vfc_cmd->flags |= cpu_to_be16(IBMVFC_READ);
1630c16b8a6dSTyrel Datwyler 		iu->add_cdb_len |= IBMVFC_RDDATA;
1631072b91f9SBrian King 	}
1632072b91f9SBrian King 
1633072b91f9SBrian King 	if (sg_mapped == 1) {
1634072b91f9SBrian King 		ibmvfc_map_sg_list(scmd, sg_mapped, data);
1635072b91f9SBrian King 		return 0;
1636072b91f9SBrian King 	}
1637072b91f9SBrian King 
16380aab6c3fSTyrel Datwyler 	vfc_cmd->flags |= cpu_to_be16(IBMVFC_SCATTERLIST);
1639072b91f9SBrian King 
1640072b91f9SBrian King 	if (!evt->ext_list) {
1641072b91f9SBrian King 		evt->ext_list = dma_pool_alloc(vhost->sg_pool, GFP_ATOMIC,
1642072b91f9SBrian King 					       &evt->ext_list_token);
1643072b91f9SBrian King 
1644072b91f9SBrian King 		if (!evt->ext_list) {
164564b840ddSBrian King 			scsi_dma_unmap(scmd);
164664b840ddSBrian King 			if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
1647072b91f9SBrian King 				scmd_printk(KERN_ERR, scmd, "Can't allocate memory for scatterlist\n");
1648072b91f9SBrian King 			return -ENOMEM;
1649072b91f9SBrian King 		}
1650072b91f9SBrian King 	}
1651072b91f9SBrian King 
1652072b91f9SBrian King 	ibmvfc_map_sg_list(scmd, sg_mapped, evt->ext_list);
1653072b91f9SBrian King 
16540aab6c3fSTyrel Datwyler 	data->va = cpu_to_be64(evt->ext_list_token);
16550aab6c3fSTyrel Datwyler 	data->len = cpu_to_be32(sg_mapped * sizeof(struct srp_direct_buf));
1656072b91f9SBrian King 	data->key = 0;
1657072b91f9SBrian King 	return 0;
1658072b91f9SBrian King }
1659072b91f9SBrian King 
1660072b91f9SBrian King /**
1661072b91f9SBrian King  * ibmvfc_timeout - Internal command timeout handler
1662dd9c7729SLee Jones  * @t:	struct ibmvfc_event that timed out
1663072b91f9SBrian King  *
1664072b91f9SBrian King  * Called when an internally generated command times out
1665072b91f9SBrian King  **/
ibmvfc_timeout(struct timer_list * t)16669a5d04fcSKees Cook static void ibmvfc_timeout(struct timer_list *t)
1667072b91f9SBrian King {
16689a5d04fcSKees Cook 	struct ibmvfc_event *evt = from_timer(evt, t, timer);
1669072b91f9SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
1670072b91f9SBrian King 	dev_err(vhost->dev, "Command timed out (%p). Resetting connection\n", evt);
1671072b91f9SBrian King 	ibmvfc_reset_host(vhost);
1672072b91f9SBrian King }
1673072b91f9SBrian King 
1674072b91f9SBrian King /**
1675072b91f9SBrian King  * ibmvfc_send_event - Transforms event to u64 array and calls send_crq()
1676072b91f9SBrian King  * @evt:		event to be sent
1677072b91f9SBrian King  * @vhost:		ibmvfc host struct
1678072b91f9SBrian King  * @timeout:	timeout in seconds - 0 means do not time command
1679072b91f9SBrian King  *
1680072b91f9SBrian King  * Returns the value returned from ibmvfc_send_crq(). (Zero for success)
1681072b91f9SBrian King  **/
ibmvfc_send_event(struct ibmvfc_event * evt,struct ibmvfc_host * vhost,unsigned long timeout)1682072b91f9SBrian King static int ibmvfc_send_event(struct ibmvfc_event *evt,
1683072b91f9SBrian King 			     struct ibmvfc_host *vhost, unsigned long timeout)
1684072b91f9SBrian King {
16850aab6c3fSTyrel Datwyler 	__be64 *crq_as_u64 = (__be64 *) &evt->crq;
168657e80e0bSTyrel Datwyler 	unsigned long flags;
1687072b91f9SBrian King 	int rc;
1688072b91f9SBrian King 
1689072b91f9SBrian King 	/* Copy the IU into the transfer area */
1690072b91f9SBrian King 	*evt->xfer_iu = evt->iu;
1691072b91f9SBrian King 	if (evt->crq.format == IBMVFC_CMD_FORMAT)
16920aab6c3fSTyrel Datwyler 		evt->xfer_iu->cmd.tag = cpu_to_be64((u64)evt);
1693072b91f9SBrian King 	else if (evt->crq.format == IBMVFC_MAD_FORMAT)
16940aab6c3fSTyrel Datwyler 		evt->xfer_iu->mad_common.tag = cpu_to_be64((u64)evt);
1695072b91f9SBrian King 	else
1696072b91f9SBrian King 		BUG();
1697072b91f9SBrian King 
16989a5d04fcSKees Cook 	timer_setup(&evt->timer, ibmvfc_timeout, 0);
1699072b91f9SBrian King 
1700072b91f9SBrian King 	if (timeout) {
1701072b91f9SBrian King 		evt->timer.expires = jiffies + (timeout * HZ);
1702072b91f9SBrian King 		add_timer(&evt->timer);
1703072b91f9SBrian King 	}
1704072b91f9SBrian King 
170557e80e0bSTyrel Datwyler 	spin_lock_irqsave(&evt->queue->l_lock, flags);
170657e80e0bSTyrel Datwyler 	list_add_tail(&evt->queue_list, &evt->queue->sent);
1707e20f80b9SBrian King 	atomic_set(&evt->active, 1);
170857e80e0bSTyrel Datwyler 
1709a528ab7aSBrian King 	mb();
1710a528ab7aSBrian King 
171131750fbdSTyrel Datwyler 	if (evt->queue->fmt == IBMVFC_SUB_CRQ_FMT)
171231750fbdSTyrel Datwyler 		rc = ibmvfc_send_sub_crq(vhost,
171331750fbdSTyrel Datwyler 					 evt->queue->vios_cookie,
171431750fbdSTyrel Datwyler 					 be64_to_cpu(crq_as_u64[0]),
171531750fbdSTyrel Datwyler 					 be64_to_cpu(crq_as_u64[1]),
171631750fbdSTyrel Datwyler 					 0, 0);
171731750fbdSTyrel Datwyler 	else
171831750fbdSTyrel Datwyler 		rc = ibmvfc_send_crq(vhost, be64_to_cpu(crq_as_u64[0]),
171931750fbdSTyrel Datwyler 				     be64_to_cpu(crq_as_u64[1]));
172031750fbdSTyrel Datwyler 
172131750fbdSTyrel Datwyler 	if (rc) {
1722e20f80b9SBrian King 		atomic_set(&evt->active, 0);
1723e4b26f3dSTyrel Datwyler 		list_del(&evt->queue_list);
172457e80e0bSTyrel Datwyler 		spin_unlock_irqrestore(&evt->queue->l_lock, flags);
1725072b91f9SBrian King 		del_timer(&evt->timer);
1726072b91f9SBrian King 
1727072b91f9SBrian King 		/* If send_crq returns H_CLOSED, return SCSI_MLQUEUE_HOST_BUSY.
1728072b91f9SBrian King 		 * Firmware will send a CRQ with a transport event (0xFF) to
1729072b91f9SBrian King 		 * tell this client what has happened to the transport. This
1730072b91f9SBrian King 		 * will be handled in ibmvfc_handle_crq()
1731072b91f9SBrian King 		 */
1732072b91f9SBrian King 		if (rc == H_CLOSED) {
1733072b91f9SBrian King 			if (printk_ratelimit())
1734072b91f9SBrian King 				dev_warn(vhost->dev, "Send warning. Receive queue closed, will retry.\n");
1735072b91f9SBrian King 			if (evt->cmnd)
1736072b91f9SBrian King 				scsi_dma_unmap(evt->cmnd);
1737072b91f9SBrian King 			ibmvfc_free_event(evt);
1738072b91f9SBrian King 			return SCSI_MLQUEUE_HOST_BUSY;
1739072b91f9SBrian King 		}
1740072b91f9SBrian King 
1741072b91f9SBrian King 		dev_err(vhost->dev, "Send error (rc=%d)\n", rc);
1742072b91f9SBrian King 		if (evt->cmnd) {
1743072b91f9SBrian King 			evt->cmnd->result = DID_ERROR << 16;
1744072b91f9SBrian King 			evt->done = ibmvfc_scsi_eh_done;
1745072b91f9SBrian King 		} else
17460aab6c3fSTyrel Datwyler 			evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR);
1747072b91f9SBrian King 
1748072b91f9SBrian King 		evt->done(evt);
174957e80e0bSTyrel Datwyler 	} else {
175057e80e0bSTyrel Datwyler 		spin_unlock_irqrestore(&evt->queue->l_lock, flags);
1751072b91f9SBrian King 		ibmvfc_trc_start(evt);
175257e80e0bSTyrel Datwyler 	}
1753072b91f9SBrian King 
1754072b91f9SBrian King 	return 0;
1755072b91f9SBrian King }
1756072b91f9SBrian King 
1757072b91f9SBrian King /**
1758072b91f9SBrian King  * ibmvfc_log_error - Log an error for the failed command if appropriate
1759072b91f9SBrian King  * @evt:	ibmvfc event to log
1760072b91f9SBrian King  *
1761072b91f9SBrian King  **/
ibmvfc_log_error(struct ibmvfc_event * evt)1762072b91f9SBrian King static void ibmvfc_log_error(struct ibmvfc_event *evt)
1763072b91f9SBrian King {
1764072b91f9SBrian King 	struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
1765072b91f9SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
17665a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd);
1767072b91f9SBrian King 	struct scsi_cmnd *cmnd = evt->cmnd;
1768072b91f9SBrian King 	const char *err = unknown_error;
17690aab6c3fSTyrel Datwyler 	int index = ibmvfc_get_err_index(be16_to_cpu(vfc_cmd->status), be16_to_cpu(vfc_cmd->error));
1770072b91f9SBrian King 	int logerr = 0;
1771072b91f9SBrian King 	int rsp_code = 0;
1772072b91f9SBrian King 
1773072b91f9SBrian King 	if (index >= 0) {
1774072b91f9SBrian King 		logerr = cmd_status[index].log;
1775072b91f9SBrian King 		err = cmd_status[index].name;
1776072b91f9SBrian King 	}
1777072b91f9SBrian King 
17780ae808e0SBrian King 	if (!logerr && (vhost->log_level <= (IBMVFC_DEFAULT_LOG_LEVEL + 1)))
1779072b91f9SBrian King 		return;
1780072b91f9SBrian King 
1781072b91f9SBrian King 	if (rsp->flags & FCP_RSP_LEN_VALID)
1782072b91f9SBrian King 		rsp_code = rsp->data.info.rsp_code;
1783072b91f9SBrian King 
17846dc6a944STyrel Datwyler 	scmd_printk(KERN_ERR, cmnd, "Command (%02X) : %s (%x:%x) "
1785072b91f9SBrian King 		    "flags: %x fcp_rsp: %x, resid=%d, scsi_status: %x\n",
17863e6f7de4STyrel Datwyler 		    cmnd->cmnd[0], err, be16_to_cpu(vfc_cmd->status), be16_to_cpu(vfc_cmd->error),
1787072b91f9SBrian King 		    rsp->flags, rsp_code, scsi_get_resid(cmnd), rsp->scsi_status);
1788072b91f9SBrian King }
1789072b91f9SBrian King 
1790072b91f9SBrian King /**
17916d29cc56SBrian King  * ibmvfc_relogin - Log back into the specified device
17926d29cc56SBrian King  * @sdev:	scsi device struct
17936d29cc56SBrian King  *
17946d29cc56SBrian King  **/
ibmvfc_relogin(struct scsi_device * sdev)17956d29cc56SBrian King static void ibmvfc_relogin(struct scsi_device *sdev)
17966d29cc56SBrian King {
17976d29cc56SBrian King 	struct ibmvfc_host *vhost = shost_priv(sdev->host);
17986d29cc56SBrian King 	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
17996d29cc56SBrian King 	struct ibmvfc_target *tgt;
18001f4a4a19STyrel Datwyler 	unsigned long flags;
18016d29cc56SBrian King 
18021f4a4a19STyrel Datwyler 	spin_lock_irqsave(vhost->host->host_lock, flags);
18036d29cc56SBrian King 	list_for_each_entry(tgt, &vhost->targets, queue) {
18046d29cc56SBrian King 		if (rport == tgt->rport) {
1805ed830385SBrian King 			ibmvfc_del_tgt(tgt);
18066d29cc56SBrian King 			break;
18076d29cc56SBrian King 		}
18086d29cc56SBrian King 	}
18096d29cc56SBrian King 
18106d29cc56SBrian King 	ibmvfc_reinit_host(vhost);
18111f4a4a19STyrel Datwyler 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
18126d29cc56SBrian King }
18136d29cc56SBrian King 
18146d29cc56SBrian King /**
1815072b91f9SBrian King  * ibmvfc_scsi_done - Handle responses from commands
1816072b91f9SBrian King  * @evt:	ibmvfc event to be handled
1817072b91f9SBrian King  *
1818072b91f9SBrian King  * Used as a callback when sending scsi cmds.
1819072b91f9SBrian King  **/
ibmvfc_scsi_done(struct ibmvfc_event * evt)1820072b91f9SBrian King static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
1821072b91f9SBrian King {
1822072b91f9SBrian King 	struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
18235a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(evt->vhost, vfc_cmd);
1824072b91f9SBrian King 	struct scsi_cmnd *cmnd = evt->cmnd;
18252bac406dSBrian King 	u32 rsp_len = 0;
18260aab6c3fSTyrel Datwyler 	u32 sense_len = be32_to_cpu(rsp->fcp_sense_len);
1827072b91f9SBrian King 
1828072b91f9SBrian King 	if (cmnd) {
18290aab6c3fSTyrel Datwyler 		if (be16_to_cpu(vfc_cmd->response_flags) & IBMVFC_ADAPTER_RESID_VALID)
18300aab6c3fSTyrel Datwyler 			scsi_set_resid(cmnd, be32_to_cpu(vfc_cmd->adapter_resid));
1831072b91f9SBrian King 		else if (rsp->flags & FCP_RESID_UNDER)
18320aab6c3fSTyrel Datwyler 			scsi_set_resid(cmnd, be32_to_cpu(rsp->fcp_resid));
1833072b91f9SBrian King 		else
1834072b91f9SBrian King 			scsi_set_resid(cmnd, 0);
1835072b91f9SBrian King 
1836072b91f9SBrian King 		if (vfc_cmd->status) {
18375a9d16f7STyrel Datwyler 			cmnd->result = ibmvfc_get_err_result(evt->vhost, vfc_cmd);
1838072b91f9SBrian King 
1839072b91f9SBrian King 			if (rsp->flags & FCP_RSP_LEN_VALID)
18400aab6c3fSTyrel Datwyler 				rsp_len = be32_to_cpu(rsp->fcp_rsp_len);
1841072b91f9SBrian King 			if ((sense_len + rsp_len) > SCSI_SENSE_BUFFERSIZE)
1842072b91f9SBrian King 				sense_len = SCSI_SENSE_BUFFERSIZE - rsp_len;
18432bac406dSBrian King 			if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len && rsp_len <= 8)
1844072b91f9SBrian King 				memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
18450aab6c3fSTyrel Datwyler 			if ((be16_to_cpu(vfc_cmd->status) & IBMVFC_VIOS_FAILURE) &&
18460aab6c3fSTyrel Datwyler 			    (be16_to_cpu(vfc_cmd->error) == IBMVFC_PLOGI_REQUIRED))
18476d29cc56SBrian King 				ibmvfc_relogin(cmnd->device);
1848072b91f9SBrian King 
184950119dadSBrian King 			if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
185050119dadSBrian King 				cmnd->result = (DID_ERROR << 16);
185150119dadSBrian King 
1852072b91f9SBrian King 			ibmvfc_log_error(evt);
1853072b91f9SBrian King 		}
1854072b91f9SBrian King 
1855072b91f9SBrian King 		if (!cmnd->result &&
1856072b91f9SBrian King 		    (scsi_bufflen(cmnd) - scsi_get_resid(cmnd) < cmnd->underflow))
1857072b91f9SBrian King 			cmnd->result = (DID_ERROR << 16);
1858072b91f9SBrian King 
1859072b91f9SBrian King 		scsi_dma_unmap(cmnd);
186085f6dd08SBart Van Assche 		scsi_done(cmnd);
1861072b91f9SBrian King 	}
1862072b91f9SBrian King 
1863072b91f9SBrian King 	ibmvfc_free_event(evt);
1864072b91f9SBrian King }
1865072b91f9SBrian King 
1866072b91f9SBrian King /**
1867072b91f9SBrian King  * ibmvfc_host_chkready - Check if the host can accept commands
1868072b91f9SBrian King  * @vhost:	 struct ibmvfc host
1869072b91f9SBrian King  *
1870072b91f9SBrian King  * Returns:
1871072b91f9SBrian King  *	1 if host can accept command / 0 if not
1872072b91f9SBrian King  **/
ibmvfc_host_chkready(struct ibmvfc_host * vhost)1873072b91f9SBrian King static inline int ibmvfc_host_chkready(struct ibmvfc_host *vhost)
1874072b91f9SBrian King {
1875072b91f9SBrian King 	int result = 0;
1876072b91f9SBrian King 
1877072b91f9SBrian King 	switch (vhost->state) {
1878072b91f9SBrian King 	case IBMVFC_LINK_DEAD:
1879072b91f9SBrian King 	case IBMVFC_HOST_OFFLINE:
1880072b91f9SBrian King 		result = DID_NO_CONNECT << 16;
1881072b91f9SBrian King 		break;
1882072b91f9SBrian King 	case IBMVFC_NO_CRQ:
1883072b91f9SBrian King 	case IBMVFC_INITIALIZING:
1884072b91f9SBrian King 	case IBMVFC_HALTED:
1885072b91f9SBrian King 	case IBMVFC_LINK_DOWN:
1886072b91f9SBrian King 		result = DID_REQUEUE << 16;
1887072b91f9SBrian King 		break;
1888072b91f9SBrian King 	case IBMVFC_ACTIVE:
1889072b91f9SBrian King 		result = 0;
1890072b91f9SBrian King 		break;
1891f36cfe6aSChristopher Díaz Riveros 	}
1892072b91f9SBrian King 
1893072b91f9SBrian King 	return result;
1894072b91f9SBrian King }
1895072b91f9SBrian King 
ibmvfc_init_vfc_cmd(struct ibmvfc_event * evt,struct scsi_device * sdev)1896fad74a1bSTyrel Datwyler static struct ibmvfc_cmd *ibmvfc_init_vfc_cmd(struct ibmvfc_event *evt, struct scsi_device *sdev)
1897fad74a1bSTyrel Datwyler {
1898fad74a1bSTyrel Datwyler 	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
18995a9d16f7STyrel Datwyler 	struct ibmvfc_host *vhost = evt->vhost;
1900fad74a1bSTyrel Datwyler 	struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd;
19015a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_cmd_iu *iu = ibmvfc_get_fcp_iu(vhost, vfc_cmd);
19025a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_rsp *rsp = ibmvfc_get_fcp_rsp(vhost, vfc_cmd);
19035a9d16f7STyrel Datwyler 	size_t offset;
1904fad74a1bSTyrel Datwyler 
1905fad74a1bSTyrel Datwyler 	memset(vfc_cmd, 0, sizeof(*vfc_cmd));
1906ebc7c74bSTyrel Datwyler 	if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
19075a9d16f7STyrel Datwyler 		offset = offsetof(struct ibmvfc_cmd, v2.rsp);
1908ebc7c74bSTyrel Datwyler 		vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name);
1909ebc7c74bSTyrel Datwyler 	} else
19105a9d16f7STyrel Datwyler 		offset = offsetof(struct ibmvfc_cmd, v1.rsp);
1911fad74a1bSTyrel Datwyler 	vfc_cmd->resp.va = cpu_to_be64(be64_to_cpu(evt->crq.ioba) + offset);
19125a9d16f7STyrel Datwyler 	vfc_cmd->resp.len = cpu_to_be32(sizeof(*rsp));
1913fad74a1bSTyrel Datwyler 	vfc_cmd->frame_type = cpu_to_be32(IBMVFC_SCSI_FCP_TYPE);
19145a9d16f7STyrel Datwyler 	vfc_cmd->payload_len = cpu_to_be32(sizeof(*iu));
19155a9d16f7STyrel Datwyler 	vfc_cmd->resp_len = cpu_to_be32(sizeof(*rsp));
1916fad74a1bSTyrel Datwyler 	vfc_cmd->cancel_key = cpu_to_be32((unsigned long)sdev->hostdata);
1917fad74a1bSTyrel Datwyler 	vfc_cmd->tgt_scsi_id = cpu_to_be64(rport->port_id);
19185a9d16f7STyrel Datwyler 	int_to_scsilun(sdev->lun, &iu->lun);
1919fad74a1bSTyrel Datwyler 
1920fad74a1bSTyrel Datwyler 	return vfc_cmd;
1921fad74a1bSTyrel Datwyler }
1922fad74a1bSTyrel Datwyler 
1923072b91f9SBrian King /**
1924072b91f9SBrian King  * ibmvfc_queuecommand - The queuecommand function of the scsi template
1925dd9c7729SLee Jones  * @shost:	scsi host struct
1926072b91f9SBrian King  * @cmnd:	struct scsi_cmnd to be executed
1927072b91f9SBrian King  *
1928072b91f9SBrian King  * Returns:
1929072b91f9SBrian King  *	0 on success / other on failure
1930072b91f9SBrian King  **/
ibmvfc_queuecommand(struct Scsi_Host * shost,struct scsi_cmnd * cmnd)1931654080d0STyrel Datwyler static int ibmvfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
1932072b91f9SBrian King {
1933654080d0STyrel Datwyler 	struct ibmvfc_host *vhost = shost_priv(shost);
1934072b91f9SBrian King 	struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
1935072b91f9SBrian King 	struct ibmvfc_cmd *vfc_cmd;
19365a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_cmd_iu *iu;
1937072b91f9SBrian King 	struct ibmvfc_event *evt;
1938e9ddad78SBart Van Assche 	u32 tag_and_hwq = blk_mq_unique_tag(scsi_cmd_to_rq(cmnd));
1939cb72477bSTyrel Datwyler 	u16 hwq = blk_mq_unique_tag_to_hwq(tag_and_hwq);
194031750fbdSTyrel Datwyler 	u16 scsi_channel;
1941072b91f9SBrian King 	int rc;
1942072b91f9SBrian King 
1943072b91f9SBrian King 	if (unlikely((rc = fc_remote_port_chkready(rport))) ||
1944072b91f9SBrian King 	    unlikely((rc = ibmvfc_host_chkready(vhost)))) {
1945072b91f9SBrian King 		cmnd->result = rc;
194685f6dd08SBart Van Assche 		scsi_done(cmnd);
1947072b91f9SBrian King 		return 0;
1948072b91f9SBrian King 	}
1949072b91f9SBrian King 
1950072b91f9SBrian King 	cmnd->result = (DID_OK << 16);
195131750fbdSTyrel Datwyler 	if (vhost->using_channels) {
195231750fbdSTyrel Datwyler 		scsi_channel = hwq % vhost->scsi_scrqs.active_queues;
195331750fbdSTyrel Datwyler 		evt = ibmvfc_get_event(&vhost->scsi_scrqs.scrqs[scsi_channel]);
1954*8bbe784cSTyrel Datwyler 		if (!evt)
1955*8bbe784cSTyrel Datwyler 			return SCSI_MLQUEUE_HOST_BUSY;
1956*8bbe784cSTyrel Datwyler 
195731750fbdSTyrel Datwyler 		evt->hwq = hwq % vhost->scsi_scrqs.active_queues;
1958*8bbe784cSTyrel Datwyler 	} else {
1959e4b26f3dSTyrel Datwyler 		evt = ibmvfc_get_event(&vhost->crq);
1960*8bbe784cSTyrel Datwyler 		if (!evt)
1961*8bbe784cSTyrel Datwyler 			return SCSI_MLQUEUE_HOST_BUSY;
1962*8bbe784cSTyrel Datwyler 	}
196331750fbdSTyrel Datwyler 
1964072b91f9SBrian King 	ibmvfc_init_event(evt, ibmvfc_scsi_done, IBMVFC_CMD_FORMAT);
1965072b91f9SBrian King 	evt->cmnd = cmnd;
1966fad74a1bSTyrel Datwyler 
1967fad74a1bSTyrel Datwyler 	vfc_cmd = ibmvfc_init_vfc_cmd(evt, cmnd->device);
19685a9d16f7STyrel Datwyler 	iu = ibmvfc_get_fcp_iu(vhost, vfc_cmd);
1969fad74a1bSTyrel Datwyler 
19705a9d16f7STyrel Datwyler 	iu->xfer_len = cpu_to_be32(scsi_bufflen(cmnd));
19715a9d16f7STyrel Datwyler 	memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len);
1972072b91f9SBrian King 
197350668633SChristoph Hellwig 	if (cmnd->flags & SCMD_TAGGED) {
19746a036ce0SJohn Garry 		vfc_cmd->task_tag = cpu_to_be64(scsi_cmd_to_rq(cmnd)->tag);
19755a9d16f7STyrel Datwyler 		iu->pri_task_attr = IBMVFC_SIMPLE_TASK;
1976072b91f9SBrian King 	}
1977072b91f9SBrian King 
1978901d01c8STyrel Datwyler 	vfc_cmd->correlation = cpu_to_be64((u64)evt);
19792aa0102cSTyrel Datwyler 
1980072b91f9SBrian King 	if (likely(!(rc = ibmvfc_map_sg_data(cmnd, evt, vfc_cmd, vhost->dev))))
1981072b91f9SBrian King 		return ibmvfc_send_event(evt, vhost, 0);
1982072b91f9SBrian King 
1983072b91f9SBrian King 	ibmvfc_free_event(evt);
1984072b91f9SBrian King 	if (rc == -ENOMEM)
1985072b91f9SBrian King 		return SCSI_MLQUEUE_HOST_BUSY;
1986072b91f9SBrian King 
1987072b91f9SBrian King 	if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
1988072b91f9SBrian King 		scmd_printk(KERN_ERR, cmnd,
1989072b91f9SBrian King 			    "Failed to map DMA buffer for command. rc=%d\n", rc);
1990072b91f9SBrian King 
1991072b91f9SBrian King 	cmnd->result = DID_ERROR << 16;
199285f6dd08SBart Van Assche 	scsi_done(cmnd);
1993072b91f9SBrian King 	return 0;
1994072b91f9SBrian King }
1995072b91f9SBrian King 
1996072b91f9SBrian King /**
1997072b91f9SBrian King  * ibmvfc_sync_completion - Signal that a synchronous command has completed
1998072b91f9SBrian King  * @evt:	ibmvfc event struct
1999072b91f9SBrian King  *
2000072b91f9SBrian King  **/
ibmvfc_sync_completion(struct ibmvfc_event * evt)2001072b91f9SBrian King static void ibmvfc_sync_completion(struct ibmvfc_event *evt)
2002072b91f9SBrian King {
2003072b91f9SBrian King 	/* copy the response back */
2004072b91f9SBrian King 	if (evt->sync_iu)
2005072b91f9SBrian King 		*evt->sync_iu = *evt->xfer_iu;
2006072b91f9SBrian King 
2007072b91f9SBrian King 	complete(&evt->comp);
2008072b91f9SBrian King }
2009072b91f9SBrian King 
2010072b91f9SBrian King /**
2011d31429e1SBrian King  * ibmvfc_bsg_timeout_done - Completion handler for cancelling BSG commands
2012d31429e1SBrian King  * @evt:	struct ibmvfc_event
2013d31429e1SBrian King  *
2014d31429e1SBrian King  **/
ibmvfc_bsg_timeout_done(struct ibmvfc_event * evt)2015d31429e1SBrian King static void ibmvfc_bsg_timeout_done(struct ibmvfc_event *evt)
2016d31429e1SBrian King {
2017d31429e1SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
2018d31429e1SBrian King 
2019d31429e1SBrian King 	ibmvfc_free_event(evt);
2020d31429e1SBrian King 	vhost->aborting_passthru = 0;
2021d31429e1SBrian King 	dev_info(vhost->dev, "Passthru command cancelled\n");
2022d31429e1SBrian King }
2023d31429e1SBrian King 
2024d31429e1SBrian King /**
2025d31429e1SBrian King  * ibmvfc_bsg_timeout - Handle a BSG timeout
202675cc8cfcSJohannes Thumshirn  * @job:	struct bsg_job that timed out
2027d31429e1SBrian King  *
2028d31429e1SBrian King  * Returns:
2029d31429e1SBrian King  *	0 on success / other on failure
2030d31429e1SBrian King  **/
ibmvfc_bsg_timeout(struct bsg_job * job)203175cc8cfcSJohannes Thumshirn static int ibmvfc_bsg_timeout(struct bsg_job *job)
2032d31429e1SBrian King {
2033cd21c605SJohannes Thumshirn 	struct ibmvfc_host *vhost = shost_priv(fc_bsg_to_shost(job));
2034d31429e1SBrian King 	unsigned long port_id = (unsigned long)job->dd_data;
2035d31429e1SBrian King 	struct ibmvfc_event *evt;
2036d31429e1SBrian King 	struct ibmvfc_tmf *tmf;
2037d31429e1SBrian King 	unsigned long flags;
2038d31429e1SBrian King 	int rc;
2039d31429e1SBrian King 
2040d31429e1SBrian King 	ENTER;
2041d31429e1SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
2042d31429e1SBrian King 	if (vhost->aborting_passthru || vhost->state != IBMVFC_ACTIVE) {
2043d31429e1SBrian King 		__ibmvfc_reset_host(vhost);
2044d31429e1SBrian King 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
2045d31429e1SBrian King 		return 0;
2046d31429e1SBrian King 	}
2047d31429e1SBrian King 
2048d31429e1SBrian King 	vhost->aborting_passthru = 1;
2049e4b26f3dSTyrel Datwyler 	evt = ibmvfc_get_event(&vhost->crq);
2050*8bbe784cSTyrel Datwyler 	if (!evt) {
2051*8bbe784cSTyrel Datwyler 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
2052*8bbe784cSTyrel Datwyler 		return -ENOMEM;
2053*8bbe784cSTyrel Datwyler 	}
2054*8bbe784cSTyrel Datwyler 
2055d31429e1SBrian King 	ibmvfc_init_event(evt, ibmvfc_bsg_timeout_done, IBMVFC_MAD_FORMAT);
2056d31429e1SBrian King 
2057d31429e1SBrian King 	tmf = &evt->iu.tmf;
2058d31429e1SBrian King 	memset(tmf, 0, sizeof(*tmf));
20590aab6c3fSTyrel Datwyler 	tmf->common.version = cpu_to_be32(1);
20600aab6c3fSTyrel Datwyler 	tmf->common.opcode = cpu_to_be32(IBMVFC_TMF_MAD);
20610aab6c3fSTyrel Datwyler 	tmf->common.length = cpu_to_be16(sizeof(*tmf));
20620aab6c3fSTyrel Datwyler 	tmf->scsi_id = cpu_to_be64(port_id);
20630aab6c3fSTyrel Datwyler 	tmf->cancel_key = cpu_to_be32(IBMVFC_PASSTHRU_CANCEL_KEY);
20640aab6c3fSTyrel Datwyler 	tmf->my_cancel_key = cpu_to_be32(IBMVFC_INTERNAL_CANCEL_KEY);
2065d31429e1SBrian King 	rc = ibmvfc_send_event(evt, vhost, default_timeout);
2066d31429e1SBrian King 
2067d31429e1SBrian King 	if (rc != 0) {
2068d31429e1SBrian King 		vhost->aborting_passthru = 0;
2069d31429e1SBrian King 		dev_err(vhost->dev, "Failed to send cancel event. rc=%d\n", rc);
2070d31429e1SBrian King 		rc = -EIO;
2071d31429e1SBrian King 	} else
2072d31429e1SBrian King 		dev_info(vhost->dev, "Cancelling passthru command to port id 0x%lx\n",
2073d31429e1SBrian King 			 port_id);
2074d31429e1SBrian King 
2075d31429e1SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
2076d31429e1SBrian King 
2077d31429e1SBrian King 	LEAVE;
2078d31429e1SBrian King 	return rc;
2079d31429e1SBrian King }
2080d31429e1SBrian King 
2081d31429e1SBrian King /**
2082d31429e1SBrian King  * ibmvfc_bsg_plogi - PLOGI into a target to handle a BSG command
2083d31429e1SBrian King  * @vhost:		struct ibmvfc_host to send command
2084d31429e1SBrian King  * @port_id:	port ID to send command
2085d31429e1SBrian King  *
2086d31429e1SBrian King  * Returns:
2087d31429e1SBrian King  *	0 on success / other on failure
2088d31429e1SBrian King  **/
ibmvfc_bsg_plogi(struct ibmvfc_host * vhost,unsigned int port_id)2089d31429e1SBrian King static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id)
2090d31429e1SBrian King {
2091d31429e1SBrian King 	struct ibmvfc_port_login *plogi;
2092d31429e1SBrian King 	struct ibmvfc_target *tgt;
2093d31429e1SBrian King 	struct ibmvfc_event *evt;
2094d31429e1SBrian King 	union ibmvfc_iu rsp_iu;
2095d31429e1SBrian King 	unsigned long flags;
2096d31429e1SBrian King 	int rc = 0, issue_login = 1;
2097d31429e1SBrian King 
2098d31429e1SBrian King 	ENTER;
2099d31429e1SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
2100d31429e1SBrian King 	list_for_each_entry(tgt, &vhost->targets, queue) {
2101d31429e1SBrian King 		if (tgt->scsi_id == port_id) {
2102d31429e1SBrian King 			issue_login = 0;
2103d31429e1SBrian King 			break;
2104d31429e1SBrian King 		}
2105d31429e1SBrian King 	}
2106d31429e1SBrian King 
2107d31429e1SBrian King 	if (!issue_login)
2108d31429e1SBrian King 		goto unlock_out;
2109d31429e1SBrian King 	if (unlikely((rc = ibmvfc_host_chkready(vhost))))
2110d31429e1SBrian King 		goto unlock_out;
2111d31429e1SBrian King 
2112e4b26f3dSTyrel Datwyler 	evt = ibmvfc_get_event(&vhost->crq);
2113*8bbe784cSTyrel Datwyler 	if (!evt) {
2114*8bbe784cSTyrel Datwyler 		rc = -ENOMEM;
2115*8bbe784cSTyrel Datwyler 		goto unlock_out;
2116*8bbe784cSTyrel Datwyler 	}
2117d31429e1SBrian King 	ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
2118d31429e1SBrian King 	plogi = &evt->iu.plogi;
2119d31429e1SBrian King 	memset(plogi, 0, sizeof(*plogi));
21200aab6c3fSTyrel Datwyler 	plogi->common.version = cpu_to_be32(1);
21210aab6c3fSTyrel Datwyler 	plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN);
21220aab6c3fSTyrel Datwyler 	plogi->common.length = cpu_to_be16(sizeof(*plogi));
21230aab6c3fSTyrel Datwyler 	plogi->scsi_id = cpu_to_be64(port_id);
2124d31429e1SBrian King 	evt->sync_iu = &rsp_iu;
2125d31429e1SBrian King 	init_completion(&evt->comp);
2126d31429e1SBrian King 
2127d31429e1SBrian King 	rc = ibmvfc_send_event(evt, vhost, default_timeout);
2128d31429e1SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
2129d31429e1SBrian King 
2130d31429e1SBrian King 	if (rc)
2131d31429e1SBrian King 		return -EIO;
2132d31429e1SBrian King 
2133d31429e1SBrian King 	wait_for_completion(&evt->comp);
2134d31429e1SBrian King 
2135d31429e1SBrian King 	if (rsp_iu.plogi.common.status)
2136d31429e1SBrian King 		rc = -EIO;
2137d31429e1SBrian King 
2138d31429e1SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
2139d31429e1SBrian King 	ibmvfc_free_event(evt);
2140d31429e1SBrian King unlock_out:
2141d31429e1SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
2142d31429e1SBrian King 	LEAVE;
2143d31429e1SBrian King 	return rc;
2144d31429e1SBrian King }
2145d31429e1SBrian King 
2146d31429e1SBrian King /**
2147d31429e1SBrian King  * ibmvfc_bsg_request - Handle a BSG request
214875cc8cfcSJohannes Thumshirn  * @job:	struct bsg_job to be executed
2149d31429e1SBrian King  *
2150d31429e1SBrian King  * Returns:
2151d31429e1SBrian King  *	0 on success / other on failure
2152d31429e1SBrian King  **/
ibmvfc_bsg_request(struct bsg_job * job)215375cc8cfcSJohannes Thumshirn static int ibmvfc_bsg_request(struct bsg_job *job)
2154d31429e1SBrian King {
2155cd21c605SJohannes Thumshirn 	struct ibmvfc_host *vhost = shost_priv(fc_bsg_to_shost(job));
21561d69b122SJohannes Thumshirn 	struct fc_rport *rport = fc_bsg_to_rport(job);
2157d31429e1SBrian King 	struct ibmvfc_passthru_mad *mad;
2158d31429e1SBrian King 	struct ibmvfc_event *evt;
2159d31429e1SBrian King 	union ibmvfc_iu rsp_iu;
2160d31429e1SBrian King 	unsigned long flags, port_id = -1;
216101e0e15cSJohannes Thumshirn 	struct fc_bsg_request *bsg_request = job->request;
216201e0e15cSJohannes Thumshirn 	struct fc_bsg_reply *bsg_reply = job->reply;
216301e0e15cSJohannes Thumshirn 	unsigned int code = bsg_request->msgcode;
2164d31429e1SBrian King 	int rc = 0, req_seg, rsp_seg, issue_login = 0;
2165d31429e1SBrian King 	u32 fc_flags, rsp_len;
2166d31429e1SBrian King 
2167d31429e1SBrian King 	ENTER;
216801e0e15cSJohannes Thumshirn 	bsg_reply->reply_payload_rcv_len = 0;
2169d31429e1SBrian King 	if (rport)
2170d31429e1SBrian King 		port_id = rport->port_id;
2171d31429e1SBrian King 
2172d31429e1SBrian King 	switch (code) {
2173d31429e1SBrian King 	case FC_BSG_HST_ELS_NOLOGIN:
217401e0e15cSJohannes Thumshirn 		port_id = (bsg_request->rqst_data.h_els.port_id[0] << 16) |
217501e0e15cSJohannes Thumshirn 			(bsg_request->rqst_data.h_els.port_id[1] << 8) |
217601e0e15cSJohannes Thumshirn 			bsg_request->rqst_data.h_els.port_id[2];
2177df561f66SGustavo A. R. Silva 		fallthrough;
2178d31429e1SBrian King 	case FC_BSG_RPT_ELS:
2179d31429e1SBrian King 		fc_flags = IBMVFC_FC_ELS;
2180d31429e1SBrian King 		break;
2181d31429e1SBrian King 	case FC_BSG_HST_CT:
2182d31429e1SBrian King 		issue_login = 1;
218301e0e15cSJohannes Thumshirn 		port_id = (bsg_request->rqst_data.h_ct.port_id[0] << 16) |
218401e0e15cSJohannes Thumshirn 			(bsg_request->rqst_data.h_ct.port_id[1] << 8) |
218501e0e15cSJohannes Thumshirn 			bsg_request->rqst_data.h_ct.port_id[2];
2186df561f66SGustavo A. R. Silva 		fallthrough;
2187d31429e1SBrian King 	case FC_BSG_RPT_CT:
2188d31429e1SBrian King 		fc_flags = IBMVFC_FC_CT_IU;
2189d31429e1SBrian King 		break;
2190d31429e1SBrian King 	default:
2191d31429e1SBrian King 		return -ENOTSUPP;
2192f36cfe6aSChristopher Díaz Riveros 	}
2193d31429e1SBrian King 
2194d31429e1SBrian King 	if (port_id == -1)
2195d31429e1SBrian King 		return -EINVAL;
2196d31429e1SBrian King 	if (!mutex_trylock(&vhost->passthru_mutex))
2197d31429e1SBrian King 		return -EBUSY;
2198d31429e1SBrian King 
2199d31429e1SBrian King 	job->dd_data = (void *)port_id;
2200d31429e1SBrian King 	req_seg = dma_map_sg(vhost->dev, job->request_payload.sg_list,
2201d31429e1SBrian King 			     job->request_payload.sg_cnt, DMA_TO_DEVICE);
2202d31429e1SBrian King 
2203d31429e1SBrian King 	if (!req_seg) {
2204d31429e1SBrian King 		mutex_unlock(&vhost->passthru_mutex);
2205d31429e1SBrian King 		return -ENOMEM;
2206d31429e1SBrian King 	}
2207d31429e1SBrian King 
2208d31429e1SBrian King 	rsp_seg = dma_map_sg(vhost->dev, job->reply_payload.sg_list,
2209d31429e1SBrian King 			     job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
2210d31429e1SBrian King 
2211d31429e1SBrian King 	if (!rsp_seg) {
2212d31429e1SBrian King 		dma_unmap_sg(vhost->dev, job->request_payload.sg_list,
2213d31429e1SBrian King 			     job->request_payload.sg_cnt, DMA_TO_DEVICE);
2214d31429e1SBrian King 		mutex_unlock(&vhost->passthru_mutex);
2215d31429e1SBrian King 		return -ENOMEM;
2216d31429e1SBrian King 	}
2217d31429e1SBrian King 
2218d31429e1SBrian King 	if (req_seg > 1 || rsp_seg > 1) {
2219d31429e1SBrian King 		rc = -EINVAL;
2220d31429e1SBrian King 		goto out;
2221d31429e1SBrian King 	}
2222d31429e1SBrian King 
2223d31429e1SBrian King 	if (issue_login)
2224d31429e1SBrian King 		rc = ibmvfc_bsg_plogi(vhost, port_id);
2225d31429e1SBrian King 
2226d31429e1SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
2227d31429e1SBrian King 
2228d31429e1SBrian King 	if (unlikely(rc || (rport && (rc = fc_remote_port_chkready(rport)))) ||
2229d31429e1SBrian King 	    unlikely((rc = ibmvfc_host_chkready(vhost)))) {
2230d31429e1SBrian King 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
2231d31429e1SBrian King 		goto out;
2232d31429e1SBrian King 	}
2233d31429e1SBrian King 
2234e4b26f3dSTyrel Datwyler 	evt = ibmvfc_get_event(&vhost->crq);
2235*8bbe784cSTyrel Datwyler 	if (!evt) {
2236*8bbe784cSTyrel Datwyler 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
2237*8bbe784cSTyrel Datwyler 		rc = -ENOMEM;
2238*8bbe784cSTyrel Datwyler 		goto out;
2239*8bbe784cSTyrel Datwyler 	}
2240d31429e1SBrian King 	ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
2241d31429e1SBrian King 	mad = &evt->iu.passthru;
2242d31429e1SBrian King 
2243d31429e1SBrian King 	memset(mad, 0, sizeof(*mad));
22440aab6c3fSTyrel Datwyler 	mad->common.version = cpu_to_be32(1);
22450aab6c3fSTyrel Datwyler 	mad->common.opcode = cpu_to_be32(IBMVFC_PASSTHRU);
22460aab6c3fSTyrel Datwyler 	mad->common.length = cpu_to_be16(sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu));
2247d31429e1SBrian King 
22480aab6c3fSTyrel Datwyler 	mad->cmd_ioba.va = cpu_to_be64(be64_to_cpu(evt->crq.ioba) +
22490aab6c3fSTyrel Datwyler 		offsetof(struct ibmvfc_passthru_mad, iu));
22500aab6c3fSTyrel Datwyler 	mad->cmd_ioba.len = cpu_to_be32(sizeof(mad->iu));
2251d31429e1SBrian King 
22520aab6c3fSTyrel Datwyler 	mad->iu.cmd_len = cpu_to_be32(job->request_payload.payload_len);
22530aab6c3fSTyrel Datwyler 	mad->iu.rsp_len = cpu_to_be32(job->reply_payload.payload_len);
22540aab6c3fSTyrel Datwyler 	mad->iu.flags = cpu_to_be32(fc_flags);
22550aab6c3fSTyrel Datwyler 	mad->iu.cancel_key = cpu_to_be32(IBMVFC_PASSTHRU_CANCEL_KEY);
2256d31429e1SBrian King 
22570aab6c3fSTyrel Datwyler 	mad->iu.cmd.va = cpu_to_be64(sg_dma_address(job->request_payload.sg_list));
22580aab6c3fSTyrel Datwyler 	mad->iu.cmd.len = cpu_to_be32(sg_dma_len(job->request_payload.sg_list));
22590aab6c3fSTyrel Datwyler 	mad->iu.rsp.va = cpu_to_be64(sg_dma_address(job->reply_payload.sg_list));
22600aab6c3fSTyrel Datwyler 	mad->iu.rsp.len = cpu_to_be32(sg_dma_len(job->reply_payload.sg_list));
22610aab6c3fSTyrel Datwyler 	mad->iu.scsi_id = cpu_to_be64(port_id);
22620aab6c3fSTyrel Datwyler 	mad->iu.tag = cpu_to_be64((u64)evt);
22630aab6c3fSTyrel Datwyler 	rsp_len = be32_to_cpu(mad->iu.rsp.len);
2264d31429e1SBrian King 
2265d31429e1SBrian King 	evt->sync_iu = &rsp_iu;
2266d31429e1SBrian King 	init_completion(&evt->comp);
2267d31429e1SBrian King 	rc = ibmvfc_send_event(evt, vhost, 0);
2268d31429e1SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
2269d31429e1SBrian King 
2270d31429e1SBrian King 	if (rc) {
2271d31429e1SBrian King 		rc = -EIO;
2272d31429e1SBrian King 		goto out;
2273d31429e1SBrian King 	}
2274d31429e1SBrian King 
2275d31429e1SBrian King 	wait_for_completion(&evt->comp);
2276d31429e1SBrian King 
2277d31429e1SBrian King 	if (rsp_iu.passthru.common.status)
2278d31429e1SBrian King 		rc = -EIO;
2279d31429e1SBrian King 	else
228001e0e15cSJohannes Thumshirn 		bsg_reply->reply_payload_rcv_len = rsp_len;
2281d31429e1SBrian King 
2282d31429e1SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
2283d31429e1SBrian King 	ibmvfc_free_event(evt);
2284d31429e1SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
228501e0e15cSJohannes Thumshirn 	bsg_reply->result = rc;
228606548160SJohannes Thumshirn 	bsg_job_done(job, bsg_reply->result,
22871abaede7SJohannes Thumshirn 		       bsg_reply->reply_payload_rcv_len);
2288d31429e1SBrian King 	rc = 0;
2289d31429e1SBrian King out:
2290d31429e1SBrian King 	dma_unmap_sg(vhost->dev, job->request_payload.sg_list,
2291d31429e1SBrian King 		     job->request_payload.sg_cnt, DMA_TO_DEVICE);
2292d31429e1SBrian King 	dma_unmap_sg(vhost->dev, job->reply_payload.sg_list,
2293d31429e1SBrian King 		     job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
2294d31429e1SBrian King 	mutex_unlock(&vhost->passthru_mutex);
2295d31429e1SBrian King 	LEAVE;
2296d31429e1SBrian King 	return rc;
2297d31429e1SBrian King }
2298d31429e1SBrian King 
2299d31429e1SBrian King /**
2300072b91f9SBrian King  * ibmvfc_reset_device - Reset the device with the specified reset type
2301072b91f9SBrian King  * @sdev:	scsi device to reset
2302072b91f9SBrian King  * @type:	reset type
2303072b91f9SBrian King  * @desc:	reset type description for log messages
2304072b91f9SBrian King  *
2305072b91f9SBrian King  * Returns:
2306072b91f9SBrian King  *	0 on success / other on failure
2307072b91f9SBrian King  **/
ibmvfc_reset_device(struct scsi_device * sdev,int type,char * desc)2308072b91f9SBrian King static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
2309072b91f9SBrian King {
2310072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(sdev->host);
2311ebc7c74bSTyrel Datwyler 	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
2312072b91f9SBrian King 	struct ibmvfc_cmd *tmf;
231350ed9a00SBrian King 	struct ibmvfc_event *evt = NULL;
2314072b91f9SBrian King 	union ibmvfc_iu rsp_iu;
23155a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_cmd_iu *iu;
23165a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_rsp *fc_rsp = ibmvfc_get_fcp_rsp(vhost, &rsp_iu.cmd);
2317072b91f9SBrian King 	int rsp_rc = -EBUSY;
2318072b91f9SBrian King 	unsigned long flags;
2319072b91f9SBrian King 	int rsp_code = 0;
2320072b91f9SBrian King 
2321072b91f9SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
2322072b91f9SBrian King 	if (vhost->state == IBMVFC_ACTIVE) {
232331750fbdSTyrel Datwyler 		if (vhost->using_channels)
232431750fbdSTyrel Datwyler 			evt = ibmvfc_get_event(&vhost->scsi_scrqs.scrqs[0]);
232531750fbdSTyrel Datwyler 		else
2326e4b26f3dSTyrel Datwyler 			evt = ibmvfc_get_event(&vhost->crq);
232731750fbdSTyrel Datwyler 
2328*8bbe784cSTyrel Datwyler 		if (!evt) {
2329*8bbe784cSTyrel Datwyler 			spin_unlock_irqrestore(vhost->host->host_lock, flags);
2330*8bbe784cSTyrel Datwyler 			return -ENOMEM;
2331*8bbe784cSTyrel Datwyler 		}
2332*8bbe784cSTyrel Datwyler 
2333072b91f9SBrian King 		ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
2334fad74a1bSTyrel Datwyler 		tmf = ibmvfc_init_vfc_cmd(evt, sdev);
23355a9d16f7STyrel Datwyler 		iu = ibmvfc_get_fcp_iu(vhost, tmf);
2336072b91f9SBrian King 
23370aab6c3fSTyrel Datwyler 		tmf->flags = cpu_to_be16((IBMVFC_NO_MEM_DESC | IBMVFC_TMF));
2338ebc7c74bSTyrel Datwyler 		if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
2339ebc7c74bSTyrel Datwyler 			tmf->target_wwpn = cpu_to_be64(rport->port_name);
23405a9d16f7STyrel Datwyler 		iu->tmf_flags = type;
2341072b91f9SBrian King 		evt->sync_iu = &rsp_iu;
2342072b91f9SBrian King 
2343072b91f9SBrian King 		init_completion(&evt->comp);
2344072b91f9SBrian King 		rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
2345072b91f9SBrian King 	}
2346072b91f9SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
2347072b91f9SBrian King 
2348072b91f9SBrian King 	if (rsp_rc != 0) {
2349072b91f9SBrian King 		sdev_printk(KERN_ERR, sdev, "Failed to send %s reset event. rc=%d\n",
2350072b91f9SBrian King 			    desc, rsp_rc);
2351072b91f9SBrian King 		return -EIO;
2352072b91f9SBrian King 	}
2353072b91f9SBrian King 
2354072b91f9SBrian King 	sdev_printk(KERN_INFO, sdev, "Resetting %s\n", desc);
2355072b91f9SBrian King 	wait_for_completion(&evt->comp);
2356072b91f9SBrian King 
2357230934a6SBrian King 	if (rsp_iu.cmd.status)
23585a9d16f7STyrel Datwyler 		rsp_code = ibmvfc_get_err_result(vhost, &rsp_iu.cmd);
2359230934a6SBrian King 
2360230934a6SBrian King 	if (rsp_code) {
2361072b91f9SBrian King 		if (fc_rsp->flags & FCP_RSP_LEN_VALID)
2362072b91f9SBrian King 			rsp_code = fc_rsp->data.info.rsp_code;
2363072b91f9SBrian King 
2364072b91f9SBrian King 		sdev_printk(KERN_ERR, sdev, "%s reset failed: %s (%x:%x) "
23650aab6c3fSTyrel Datwyler 			    "flags: %x fcp_rsp: %x, scsi_status: %x\n", desc,
23660aab6c3fSTyrel Datwyler 			    ibmvfc_get_cmd_error(be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error)),
23673e6f7de4STyrel Datwyler 			    be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error), fc_rsp->flags, rsp_code,
2368072b91f9SBrian King 			    fc_rsp->scsi_status);
2369072b91f9SBrian King 		rsp_rc = -EIO;
2370072b91f9SBrian King 	} else
2371072b91f9SBrian King 		sdev_printk(KERN_INFO, sdev, "%s reset successful\n", desc);
2372072b91f9SBrian King 
2373072b91f9SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
2374072b91f9SBrian King 	ibmvfc_free_event(evt);
2375072b91f9SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
2376072b91f9SBrian King 	return rsp_rc;
2377072b91f9SBrian King }
2378072b91f9SBrian King 
2379072b91f9SBrian King /**
2380d2fab5cfSBrian King  * ibmvfc_match_rport - Match function for specified remote port
2381d2fab5cfSBrian King  * @evt:	ibmvfc event struct
2382dd9c7729SLee Jones  * @rport:	device to match
2383072b91f9SBrian King  *
2384072b91f9SBrian King  * Returns:
2385d2fab5cfSBrian King  *	1 if event matches rport / 0 if event does not match rport
2386072b91f9SBrian King  **/
ibmvfc_match_rport(struct ibmvfc_event * evt,void * rport)2387d2fab5cfSBrian King static int ibmvfc_match_rport(struct ibmvfc_event *evt, void *rport)
2388072b91f9SBrian King {
2389d2fab5cfSBrian King 	struct fc_rport *cmd_rport;
2390072b91f9SBrian King 
2391d2fab5cfSBrian King 	if (evt->cmnd) {
2392d2fab5cfSBrian King 		cmd_rport = starget_to_rport(scsi_target(evt->cmnd->device));
2393d2fab5cfSBrian King 		if (cmd_rport == rport)
2394d2fab5cfSBrian King 			return 1;
2395072b91f9SBrian King 	}
2396072b91f9SBrian King 	return 0;
2397072b91f9SBrian King }
2398072b91f9SBrian King 
2399072b91f9SBrian King /**
2400ad8dcffaSBrian King  * ibmvfc_match_target - Match function for specified target
2401ad8dcffaSBrian King  * @evt:	ibmvfc event struct
2402ad8dcffaSBrian King  * @device:	device to match (starget)
2403ad8dcffaSBrian King  *
2404ad8dcffaSBrian King  * Returns:
2405ad8dcffaSBrian King  *	1 if event matches starget / 0 if event does not match starget
2406ad8dcffaSBrian King  **/
ibmvfc_match_target(struct ibmvfc_event * evt,void * device)2407ad8dcffaSBrian King static int ibmvfc_match_target(struct ibmvfc_event *evt, void *device)
2408ad8dcffaSBrian King {
2409ad8dcffaSBrian King 	if (evt->cmnd && scsi_target(evt->cmnd->device) == device)
2410ad8dcffaSBrian King 		return 1;
2411ad8dcffaSBrian King 	return 0;
2412ad8dcffaSBrian King }
2413ad8dcffaSBrian King 
2414ad8dcffaSBrian King /**
2415ad8dcffaSBrian King  * ibmvfc_match_lun - Match function for specified LUN
2416ad8dcffaSBrian King  * @evt:	ibmvfc event struct
2417ad8dcffaSBrian King  * @device:	device to match (sdev)
2418ad8dcffaSBrian King  *
2419ad8dcffaSBrian King  * Returns:
2420ad8dcffaSBrian King  *	1 if event matches sdev / 0 if event does not match sdev
2421ad8dcffaSBrian King  **/
ibmvfc_match_lun(struct ibmvfc_event * evt,void * device)2422ad8dcffaSBrian King static int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
2423ad8dcffaSBrian King {
2424ad8dcffaSBrian King 	if (evt->cmnd && evt->cmnd->device == device)
2425ad8dcffaSBrian King 		return 1;
2426ad8dcffaSBrian King 	return 0;
2427ad8dcffaSBrian King }
2428ad8dcffaSBrian King 
2429ad8dcffaSBrian King /**
24308b1c9b20STyrel Datwyler  * ibmvfc_event_is_free - Check if event is free or not
24318b1c9b20STyrel Datwyler  * @evt:	ibmvfc event struct
24328b1c9b20STyrel Datwyler  *
24338b1c9b20STyrel Datwyler  * Returns:
24348b1c9b20STyrel Datwyler  *	true / false
24358b1c9b20STyrel Datwyler  **/
ibmvfc_event_is_free(struct ibmvfc_event * evt)24368b1c9b20STyrel Datwyler static bool ibmvfc_event_is_free(struct ibmvfc_event *evt)
24378b1c9b20STyrel Datwyler {
24388b1c9b20STyrel Datwyler 	struct ibmvfc_event *loop_evt;
24398b1c9b20STyrel Datwyler 
24408b1c9b20STyrel Datwyler 	list_for_each_entry(loop_evt, &evt->queue->free, queue_list)
24418b1c9b20STyrel Datwyler 		if (loop_evt == evt)
24428b1c9b20STyrel Datwyler 			return true;
24438b1c9b20STyrel Datwyler 
24448b1c9b20STyrel Datwyler 	return false;
24458b1c9b20STyrel Datwyler }
24468b1c9b20STyrel Datwyler 
24478b1c9b20STyrel Datwyler /**
2448ad8dcffaSBrian King  * ibmvfc_wait_for_ops - Wait for ops to complete
2449ad8dcffaSBrian King  * @vhost:	ibmvfc host struct
2450ad8dcffaSBrian King  * @device:	device to match (starget or sdev)
2451ad8dcffaSBrian King  * @match:	match function
2452ad8dcffaSBrian King  *
2453ad8dcffaSBrian King  * Returns:
2454ad8dcffaSBrian King  *	SUCCESS / FAILED
2455ad8dcffaSBrian King  **/
ibmvfc_wait_for_ops(struct ibmvfc_host * vhost,void * device,int (* match)(struct ibmvfc_event *,void *))2456ad8dcffaSBrian King static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
2457ad8dcffaSBrian King 			       int (*match) (struct ibmvfc_event *, void *))
2458ad8dcffaSBrian King {
2459ad8dcffaSBrian King 	struct ibmvfc_event *evt;
2460ad8dcffaSBrian King 	DECLARE_COMPLETION_ONSTACK(comp);
246162fc2661STyrel Datwyler 	int wait, i, q_index, q_size;
2462ad8dcffaSBrian King 	unsigned long flags;
2463daa142d1SBrian King 	signed long timeout = IBMVFC_ABORT_WAIT_TIMEOUT * HZ;
246462fc2661STyrel Datwyler 	struct ibmvfc_queue *queues;
2465ad8dcffaSBrian King 
2466ad8dcffaSBrian King 	ENTER;
246762fc2661STyrel Datwyler 	if (vhost->mq_enabled && vhost->using_channels) {
246862fc2661STyrel Datwyler 		queues = vhost->scsi_scrqs.scrqs;
246962fc2661STyrel Datwyler 		q_size = vhost->scsi_scrqs.active_queues;
247062fc2661STyrel Datwyler 	} else {
247162fc2661STyrel Datwyler 		queues = &vhost->crq;
247262fc2661STyrel Datwyler 		q_size = 1;
247362fc2661STyrel Datwyler 	}
247462fc2661STyrel Datwyler 
2475ad8dcffaSBrian King 	do {
2476ad8dcffaSBrian King 		wait = 0;
247762fc2661STyrel Datwyler 		spin_lock_irqsave(vhost->host->host_lock, flags);
247862fc2661STyrel Datwyler 		for (q_index = 0; q_index < q_size; q_index++) {
247962fc2661STyrel Datwyler 			spin_lock(&queues[q_index].l_lock);
248062fc2661STyrel Datwyler 			for (i = 0; i < queues[q_index].evt_pool.size; i++) {
248162fc2661STyrel Datwyler 				evt = &queues[q_index].evt_pool.events[i];
24828b1c9b20STyrel Datwyler 				if (!ibmvfc_event_is_free(evt)) {
2483ad8dcffaSBrian King 					if (match(evt, device)) {
2484ad8dcffaSBrian King 						evt->eh_comp = &comp;
2485ad8dcffaSBrian King 						wait++;
2486ad8dcffaSBrian King 					}
2487ad8dcffaSBrian King 				}
24888b1c9b20STyrel Datwyler 			}
248962fc2661STyrel Datwyler 			spin_unlock(&queues[q_index].l_lock);
249062fc2661STyrel Datwyler 		}
249162fc2661STyrel Datwyler 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
2492ad8dcffaSBrian King 
2493ad8dcffaSBrian King 		if (wait) {
2494ad8dcffaSBrian King 			timeout = wait_for_completion_timeout(&comp, timeout);
2495ad8dcffaSBrian King 
2496ad8dcffaSBrian King 			if (!timeout) {
2497ad8dcffaSBrian King 				wait = 0;
249862fc2661STyrel Datwyler 				spin_lock_irqsave(vhost->host->host_lock, flags);
249962fc2661STyrel Datwyler 				for (q_index = 0; q_index < q_size; q_index++) {
250062fc2661STyrel Datwyler 					spin_lock(&queues[q_index].l_lock);
250162fc2661STyrel Datwyler 					for (i = 0; i < queues[q_index].evt_pool.size; i++) {
250262fc2661STyrel Datwyler 						evt = &queues[q_index].evt_pool.events[i];
25038b1c9b20STyrel Datwyler 						if (!ibmvfc_event_is_free(evt)) {
2504ad8dcffaSBrian King 							if (match(evt, device)) {
2505ad8dcffaSBrian King 								evt->eh_comp = NULL;
2506ad8dcffaSBrian King 								wait++;
2507ad8dcffaSBrian King 							}
2508ad8dcffaSBrian King 						}
25098b1c9b20STyrel Datwyler 					}
251062fc2661STyrel Datwyler 					spin_unlock(&queues[q_index].l_lock);
251162fc2661STyrel Datwyler 				}
251262fc2661STyrel Datwyler 				spin_unlock_irqrestore(vhost->host->host_lock, flags);
2513ad8dcffaSBrian King 				if (wait)
2514ad8dcffaSBrian King 					dev_err(vhost->dev, "Timed out waiting for aborted commands\n");
2515ad8dcffaSBrian King 				LEAVE;
2516ad8dcffaSBrian King 				return wait ? FAILED : SUCCESS;
2517ad8dcffaSBrian King 			}
2518ad8dcffaSBrian King 		}
2519ad8dcffaSBrian King 	} while (wait);
2520ad8dcffaSBrian King 
2521ad8dcffaSBrian King 	LEAVE;
2522ad8dcffaSBrian King 	return SUCCESS;
2523ad8dcffaSBrian King }
2524ad8dcffaSBrian King 
ibmvfc_init_tmf(struct ibmvfc_queue * queue,struct scsi_device * sdev,int type)2525a61236daSTyrel Datwyler static struct ibmvfc_event *ibmvfc_init_tmf(struct ibmvfc_queue *queue,
2526a61236daSTyrel Datwyler 					    struct scsi_device *sdev,
2527a61236daSTyrel Datwyler 					    int type)
2528a61236daSTyrel Datwyler {
2529a61236daSTyrel Datwyler 	struct ibmvfc_host *vhost = shost_priv(sdev->host);
2530a61236daSTyrel Datwyler 	struct scsi_target *starget = scsi_target(sdev);
2531a61236daSTyrel Datwyler 	struct fc_rport *rport = starget_to_rport(starget);
2532a61236daSTyrel Datwyler 	struct ibmvfc_event *evt;
2533a61236daSTyrel Datwyler 	struct ibmvfc_tmf *tmf;
2534a61236daSTyrel Datwyler 
2535a61236daSTyrel Datwyler 	evt = ibmvfc_get_event(queue);
2536*8bbe784cSTyrel Datwyler 	if (!evt)
2537*8bbe784cSTyrel Datwyler 		return NULL;
2538a61236daSTyrel Datwyler 	ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
2539a61236daSTyrel Datwyler 
2540a61236daSTyrel Datwyler 	tmf = &evt->iu.tmf;
2541a61236daSTyrel Datwyler 	memset(tmf, 0, sizeof(*tmf));
2542a61236daSTyrel Datwyler 	if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
2543a61236daSTyrel Datwyler 		tmf->common.version = cpu_to_be32(2);
2544a61236daSTyrel Datwyler 		tmf->target_wwpn = cpu_to_be64(rport->port_name);
2545a61236daSTyrel Datwyler 	} else {
2546a61236daSTyrel Datwyler 		tmf->common.version = cpu_to_be32(1);
2547a61236daSTyrel Datwyler 	}
2548a61236daSTyrel Datwyler 	tmf->common.opcode = cpu_to_be32(IBMVFC_TMF_MAD);
2549a61236daSTyrel Datwyler 	tmf->common.length = cpu_to_be16(sizeof(*tmf));
2550a61236daSTyrel Datwyler 	tmf->scsi_id = cpu_to_be64(rport->port_id);
2551a61236daSTyrel Datwyler 	int_to_scsilun(sdev->lun, &tmf->lun);
2552a61236daSTyrel Datwyler 	if (!ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPRESS_ABTS))
2553a61236daSTyrel Datwyler 		type &= ~IBMVFC_TMF_SUPPRESS_ABTS;
2554a61236daSTyrel Datwyler 	if (vhost->state == IBMVFC_ACTIVE)
2555a61236daSTyrel Datwyler 		tmf->flags = cpu_to_be32((type | IBMVFC_TMF_LUA_VALID));
2556a61236daSTyrel Datwyler 	else
2557a61236daSTyrel Datwyler 		tmf->flags = cpu_to_be32(((type & IBMVFC_TMF_SUPPRESS_ABTS) | IBMVFC_TMF_LUA_VALID));
2558a61236daSTyrel Datwyler 	tmf->cancel_key = cpu_to_be32((unsigned long)sdev->hostdata);
2559a61236daSTyrel Datwyler 	tmf->my_cancel_key = cpu_to_be32((unsigned long)starget->hostdata);
2560a61236daSTyrel Datwyler 
2561a61236daSTyrel Datwyler 	init_completion(&evt->comp);
2562a61236daSTyrel Datwyler 
2563a61236daSTyrel Datwyler 	return evt;
2564a61236daSTyrel Datwyler }
2565a61236daSTyrel Datwyler 
ibmvfc_cancel_all_mq(struct scsi_device * sdev,int type)2566a835f386STyrel Datwyler static int ibmvfc_cancel_all_mq(struct scsi_device *sdev, int type)
2567a835f386STyrel Datwyler {
2568a835f386STyrel Datwyler 	struct ibmvfc_host *vhost = shost_priv(sdev->host);
2569a835f386STyrel Datwyler 	struct ibmvfc_event *evt, *found_evt, *temp;
2570a835f386STyrel Datwyler 	struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs;
2571a835f386STyrel Datwyler 	unsigned long flags;
2572a835f386STyrel Datwyler 	int num_hwq, i;
2573a835f386STyrel Datwyler 	int fail = 0;
2574a835f386STyrel Datwyler 	LIST_HEAD(cancelq);
2575a835f386STyrel Datwyler 	u16 status;
2576a835f386STyrel Datwyler 
2577a835f386STyrel Datwyler 	ENTER;
2578a835f386STyrel Datwyler 	spin_lock_irqsave(vhost->host->host_lock, flags);
2579a835f386STyrel Datwyler 	num_hwq = vhost->scsi_scrqs.active_queues;
2580a835f386STyrel Datwyler 	for (i = 0; i < num_hwq; i++) {
2581a835f386STyrel Datwyler 		spin_lock(queues[i].q_lock);
2582a835f386STyrel Datwyler 		spin_lock(&queues[i].l_lock);
2583a835f386STyrel Datwyler 		found_evt = NULL;
2584a835f386STyrel Datwyler 		list_for_each_entry(evt, &queues[i].sent, queue_list) {
2585a835f386STyrel Datwyler 			if (evt->cmnd && evt->cmnd->device == sdev) {
2586a835f386STyrel Datwyler 				found_evt = evt;
2587a835f386STyrel Datwyler 				break;
2588a835f386STyrel Datwyler 			}
2589a835f386STyrel Datwyler 		}
2590a835f386STyrel Datwyler 		spin_unlock(&queues[i].l_lock);
2591a835f386STyrel Datwyler 
2592a835f386STyrel Datwyler 		if (found_evt && vhost->logged_in) {
2593a835f386STyrel Datwyler 			evt = ibmvfc_init_tmf(&queues[i], sdev, type);
2594*8bbe784cSTyrel Datwyler 			if (!evt) {
2595*8bbe784cSTyrel Datwyler 				spin_unlock(queues[i].q_lock);
2596*8bbe784cSTyrel Datwyler 				spin_unlock_irqrestore(vhost->host->host_lock, flags);
2597*8bbe784cSTyrel Datwyler 				return -ENOMEM;
2598*8bbe784cSTyrel Datwyler 			}
2599a835f386STyrel Datwyler 			evt->sync_iu = &queues[i].cancel_rsp;
2600a835f386STyrel Datwyler 			ibmvfc_send_event(evt, vhost, default_timeout);
2601a835f386STyrel Datwyler 			list_add_tail(&evt->cancel, &cancelq);
2602a835f386STyrel Datwyler 		}
2603a835f386STyrel Datwyler 
2604a835f386STyrel Datwyler 		spin_unlock(queues[i].q_lock);
2605a835f386STyrel Datwyler 	}
2606a835f386STyrel Datwyler 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
2607a835f386STyrel Datwyler 
2608a835f386STyrel Datwyler 	if (list_empty(&cancelq)) {
2609a835f386STyrel Datwyler 		if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
2610a835f386STyrel Datwyler 			sdev_printk(KERN_INFO, sdev, "No events found to cancel\n");
2611a835f386STyrel Datwyler 		return 0;
2612a835f386STyrel Datwyler 	}
2613a835f386STyrel Datwyler 
2614a835f386STyrel Datwyler 	sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n");
2615a835f386STyrel Datwyler 
2616a835f386STyrel Datwyler 	list_for_each_entry_safe(evt, temp, &cancelq, cancel) {
2617a835f386STyrel Datwyler 		wait_for_completion(&evt->comp);
2618a835f386STyrel Datwyler 		status = be16_to_cpu(evt->queue->cancel_rsp.mad_common.status);
2619a835f386STyrel Datwyler 		list_del(&evt->cancel);
2620a835f386STyrel Datwyler 		ibmvfc_free_event(evt);
2621a835f386STyrel Datwyler 
2622a835f386STyrel Datwyler 		if (status != IBMVFC_MAD_SUCCESS) {
2623a835f386STyrel Datwyler 			sdev_printk(KERN_WARNING, sdev, "Cancel failed with rc=%x\n", status);
2624a835f386STyrel Datwyler 			switch (status) {
2625a835f386STyrel Datwyler 			case IBMVFC_MAD_DRIVER_FAILED:
2626a835f386STyrel Datwyler 			case IBMVFC_MAD_CRQ_ERROR:
2627a835f386STyrel Datwyler 			/* Host adapter most likely going through reset, return success to
2628a835f386STyrel Datwyler 			 * the caller will wait for the command being cancelled to get returned
2629a835f386STyrel Datwyler 			 */
2630a835f386STyrel Datwyler 				break;
2631a835f386STyrel Datwyler 			default:
2632a835f386STyrel Datwyler 				fail = 1;
2633a835f386STyrel Datwyler 				break;
2634a835f386STyrel Datwyler 			}
2635a835f386STyrel Datwyler 		}
2636a835f386STyrel Datwyler 	}
2637a835f386STyrel Datwyler 
2638a835f386STyrel Datwyler 	if (fail)
2639a835f386STyrel Datwyler 		return -EIO;
2640a835f386STyrel Datwyler 
2641a835f386STyrel Datwyler 	sdev_printk(KERN_INFO, sdev, "Successfully cancelled outstanding commands\n");
2642a835f386STyrel Datwyler 	LEAVE;
2643a835f386STyrel Datwyler 	return 0;
2644a835f386STyrel Datwyler }
2645a835f386STyrel Datwyler 
ibmvfc_cancel_all_sq(struct scsi_device * sdev,int type)2646a835f386STyrel Datwyler static int ibmvfc_cancel_all_sq(struct scsi_device *sdev, int type)
2647d2fab5cfSBrian King {
2648d2fab5cfSBrian King 	struct ibmvfc_host *vhost = shost_priv(sdev->host);
2649d2fab5cfSBrian King 	struct ibmvfc_event *evt, *found_evt;
2650d2fab5cfSBrian King 	union ibmvfc_iu rsp;
2651d2fab5cfSBrian King 	int rsp_rc = -EBUSY;
2652d2fab5cfSBrian King 	unsigned long flags;
2653d2fab5cfSBrian King 	u16 status;
2654d2fab5cfSBrian King 
2655d2fab5cfSBrian King 	ENTER;
2656d2fab5cfSBrian King 	found_evt = NULL;
265757e80e0bSTyrel Datwyler 	spin_lock_irqsave(vhost->host->host_lock, flags);
265857e80e0bSTyrel Datwyler 	spin_lock(&vhost->crq.l_lock);
2659e4b26f3dSTyrel Datwyler 	list_for_each_entry(evt, &vhost->crq.sent, queue_list) {
2660d2fab5cfSBrian King 		if (evt->cmnd && evt->cmnd->device == sdev) {
2661d2fab5cfSBrian King 			found_evt = evt;
2662d2fab5cfSBrian King 			break;
2663d2fab5cfSBrian King 		}
2664d2fab5cfSBrian King 	}
266557e80e0bSTyrel Datwyler 	spin_unlock(&vhost->crq.l_lock);
2666d2fab5cfSBrian King 
2667d2fab5cfSBrian King 	if (!found_evt) {
2668d2fab5cfSBrian King 		if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
2669d2fab5cfSBrian King 			sdev_printk(KERN_INFO, sdev, "No events found to cancel\n");
2670d2fab5cfSBrian King 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
2671d2fab5cfSBrian King 		return 0;
2672d2fab5cfSBrian King 	}
2673d2fab5cfSBrian King 
267455d29bf0SBrian King 	if (vhost->logged_in) {
2675a61236daSTyrel Datwyler 		evt = ibmvfc_init_tmf(&vhost->crq, sdev, type);
2676d2fab5cfSBrian King 		evt->sync_iu = &rsp;
2677d2fab5cfSBrian King 		rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
2678d2fab5cfSBrian King 	}
2679d2fab5cfSBrian King 
2680d2fab5cfSBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
2681d2fab5cfSBrian King 
2682d2fab5cfSBrian King 	if (rsp_rc != 0) {
2683d2fab5cfSBrian King 		sdev_printk(KERN_ERR, sdev, "Failed to send cancel event. rc=%d\n", rsp_rc);
2684c281e32aSBrian King 		/* If failure is received, the host adapter is most likely going
2685c281e32aSBrian King 		 through reset, return success so the caller will wait for the command
2686c281e32aSBrian King 		 being cancelled to get returned */
2687c281e32aSBrian King 		return 0;
2688d2fab5cfSBrian King 	}
2689d2fab5cfSBrian King 
2690d2fab5cfSBrian King 	sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n");
2691d2fab5cfSBrian King 
2692d2fab5cfSBrian King 	wait_for_completion(&evt->comp);
26930aab6c3fSTyrel Datwyler 	status = be16_to_cpu(rsp.mad_common.status);
2694d2fab5cfSBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
2695d2fab5cfSBrian King 	ibmvfc_free_event(evt);
2696d2fab5cfSBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
2697d2fab5cfSBrian King 
2698d2fab5cfSBrian King 	if (status != IBMVFC_MAD_SUCCESS) {
2699d2fab5cfSBrian King 		sdev_printk(KERN_WARNING, sdev, "Cancel failed with rc=%x\n", status);
2700c281e32aSBrian King 		switch (status) {
2701c281e32aSBrian King 		case IBMVFC_MAD_DRIVER_FAILED:
2702c281e32aSBrian King 		case IBMVFC_MAD_CRQ_ERROR:
2703c281e32aSBrian King 			/* Host adapter most likely going through reset, return success to
2704c281e32aSBrian King 			 the caller will wait for the command being cancelled to get returned */
2705c281e32aSBrian King 			return 0;
2706c281e32aSBrian King 		default:
2707d2fab5cfSBrian King 			return -EIO;
2708c281e32aSBrian King 		};
2709d2fab5cfSBrian King 	}
2710d2fab5cfSBrian King 
2711d2fab5cfSBrian King 	sdev_printk(KERN_INFO, sdev, "Successfully cancelled outstanding commands\n");
2712d2fab5cfSBrian King 	return 0;
2713d2fab5cfSBrian King }
2714d2fab5cfSBrian King 
2715d2fab5cfSBrian King /**
2716a835f386STyrel Datwyler  * ibmvfc_cancel_all - Cancel all outstanding commands to the device
2717a835f386STyrel Datwyler  * @sdev:	scsi device to cancel commands
2718a835f386STyrel Datwyler  * @type:	type of error recovery being performed
2719a835f386STyrel Datwyler  *
2720a835f386STyrel Datwyler  * This sends a cancel to the VIOS for the specified device. This does
2721a835f386STyrel Datwyler  * NOT send any abort to the actual device. That must be done separately.
2722a835f386STyrel Datwyler  *
2723a835f386STyrel Datwyler  * Returns:
2724a835f386STyrel Datwyler  *	0 on success / other on failure
2725a835f386STyrel Datwyler  **/
ibmvfc_cancel_all(struct scsi_device * sdev,int type)2726a835f386STyrel Datwyler static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
2727a835f386STyrel Datwyler {
2728a835f386STyrel Datwyler 	struct ibmvfc_host *vhost = shost_priv(sdev->host);
2729a835f386STyrel Datwyler 
2730a835f386STyrel Datwyler 	if (vhost->mq_enabled && vhost->using_channels)
2731a835f386STyrel Datwyler 		return ibmvfc_cancel_all_mq(sdev, type);
2732a835f386STyrel Datwyler 	else
2733a835f386STyrel Datwyler 		return ibmvfc_cancel_all_sq(sdev, type);
2734a835f386STyrel Datwyler }
2735a835f386STyrel Datwyler 
2736a835f386STyrel Datwyler /**
2737d2fab5cfSBrian King  * ibmvfc_match_key - Match function for specified cancel key
2738d2fab5cfSBrian King  * @evt:	ibmvfc event struct
2739d2fab5cfSBrian King  * @key:	cancel key to match
2740d2fab5cfSBrian King  *
2741d2fab5cfSBrian King  * Returns:
2742d2fab5cfSBrian King  *	1 if event matches key / 0 if event does not match key
2743d2fab5cfSBrian King  **/
ibmvfc_match_key(struct ibmvfc_event * evt,void * key)2744d2fab5cfSBrian King static int ibmvfc_match_key(struct ibmvfc_event *evt, void *key)
2745d2fab5cfSBrian King {
2746d2fab5cfSBrian King 	unsigned long cancel_key = (unsigned long)key;
2747d2fab5cfSBrian King 
2748d2fab5cfSBrian King 	if (evt->crq.format == IBMVFC_CMD_FORMAT &&
27490aab6c3fSTyrel Datwyler 	    be32_to_cpu(evt->iu.cmd.cancel_key) == cancel_key)
2750d2fab5cfSBrian King 		return 1;
2751d2fab5cfSBrian King 	return 0;
2752d2fab5cfSBrian King }
2753d2fab5cfSBrian King 
2754d2fab5cfSBrian King /**
2755a077c7faSBrian King  * ibmvfc_match_evt - Match function for specified event
2756a077c7faSBrian King  * @evt:	ibmvfc event struct
2757a077c7faSBrian King  * @match:	event to match
2758a077c7faSBrian King  *
2759a077c7faSBrian King  * Returns:
2760a077c7faSBrian King  *	1 if event matches key / 0 if event does not match key
2761a077c7faSBrian King  **/
ibmvfc_match_evt(struct ibmvfc_event * evt,void * match)2762a077c7faSBrian King static int ibmvfc_match_evt(struct ibmvfc_event *evt, void *match)
2763a077c7faSBrian King {
2764a077c7faSBrian King 	if (evt == match)
2765a077c7faSBrian King 		return 1;
2766a077c7faSBrian King 	return 0;
2767a077c7faSBrian King }
2768a077c7faSBrian King 
2769a077c7faSBrian King /**
2770d2fab5cfSBrian King  * ibmvfc_abort_task_set - Abort outstanding commands to the device
2771d2fab5cfSBrian King  * @sdev:	scsi device to abort commands
2772d2fab5cfSBrian King  *
2773d2fab5cfSBrian King  * This sends an Abort Task Set to the VIOS for the specified device. This does
2774d2fab5cfSBrian King  * NOT send any cancel to the VIOS. That must be done separately.
2775d2fab5cfSBrian King  *
2776d2fab5cfSBrian King  * Returns:
2777d2fab5cfSBrian King  *	0 on success / other on failure
2778d2fab5cfSBrian King  **/
ibmvfc_abort_task_set(struct scsi_device * sdev)2779d2fab5cfSBrian King static int ibmvfc_abort_task_set(struct scsi_device *sdev)
2780d2fab5cfSBrian King {
2781d2fab5cfSBrian King 	struct ibmvfc_host *vhost = shost_priv(sdev->host);
2782ebc7c74bSTyrel Datwyler 	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
2783d2fab5cfSBrian King 	struct ibmvfc_cmd *tmf;
2784d2fab5cfSBrian King 	struct ibmvfc_event *evt, *found_evt;
2785d2fab5cfSBrian King 	union ibmvfc_iu rsp_iu;
27865a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_cmd_iu *iu;
27875a9d16f7STyrel Datwyler 	struct ibmvfc_fcp_rsp *fc_rsp = ibmvfc_get_fcp_rsp(vhost, &rsp_iu.cmd);
2788d2fab5cfSBrian King 	int rc, rsp_rc = -EBUSY;
2789d2fab5cfSBrian King 	unsigned long flags, timeout = IBMVFC_ABORT_TIMEOUT;
2790d2fab5cfSBrian King 	int rsp_code = 0;
2791d2fab5cfSBrian King 
2792d2fab5cfSBrian King 	found_evt = NULL;
279357e80e0bSTyrel Datwyler 	spin_lock_irqsave(vhost->host->host_lock, flags);
279457e80e0bSTyrel Datwyler 	spin_lock(&vhost->crq.l_lock);
2795e4b26f3dSTyrel Datwyler 	list_for_each_entry(evt, &vhost->crq.sent, queue_list) {
2796d2fab5cfSBrian King 		if (evt->cmnd && evt->cmnd->device == sdev) {
2797d2fab5cfSBrian King 			found_evt = evt;
2798d2fab5cfSBrian King 			break;
2799d2fab5cfSBrian King 		}
2800d2fab5cfSBrian King 	}
280157e80e0bSTyrel Datwyler 	spin_unlock(&vhost->crq.l_lock);
2802d2fab5cfSBrian King 
2803d2fab5cfSBrian King 	if (!found_evt) {
2804d2fab5cfSBrian King 		if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
2805d2fab5cfSBrian King 			sdev_printk(KERN_INFO, sdev, "No events found to abort\n");
2806d2fab5cfSBrian King 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
2807d2fab5cfSBrian King 		return 0;
2808d2fab5cfSBrian King 	}
2809d2fab5cfSBrian King 
2810d2fab5cfSBrian King 	if (vhost->state == IBMVFC_ACTIVE) {
2811e4b26f3dSTyrel Datwyler 		evt = ibmvfc_get_event(&vhost->crq);
2812*8bbe784cSTyrel Datwyler 		if (!evt) {
2813*8bbe784cSTyrel Datwyler 			spin_unlock_irqrestore(vhost->host->host_lock, flags);
2814*8bbe784cSTyrel Datwyler 			return -ENOMEM;
2815*8bbe784cSTyrel Datwyler 		}
2816d2fab5cfSBrian King 		ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
2817fad74a1bSTyrel Datwyler 		tmf = ibmvfc_init_vfc_cmd(evt, sdev);
28185a9d16f7STyrel Datwyler 		iu = ibmvfc_get_fcp_iu(vhost, tmf);
2819d2fab5cfSBrian King 
2820ebc7c74bSTyrel Datwyler 		if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
2821ebc7c74bSTyrel Datwyler 			tmf->target_wwpn = cpu_to_be64(rport->port_name);
28225a9d16f7STyrel Datwyler 		iu->tmf_flags = IBMVFC_ABORT_TASK_SET;
28230aab6c3fSTyrel Datwyler 		tmf->flags = cpu_to_be16((IBMVFC_NO_MEM_DESC | IBMVFC_TMF));
2824d2fab5cfSBrian King 		evt->sync_iu = &rsp_iu;
2825d2fab5cfSBrian King 
2826901d01c8STyrel Datwyler 		tmf->correlation = cpu_to_be64((u64)evt);
28272aa0102cSTyrel Datwyler 
2828d2fab5cfSBrian King 		init_completion(&evt->comp);
2829d2fab5cfSBrian King 		rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
2830d2fab5cfSBrian King 	}
2831d2fab5cfSBrian King 
2832d2fab5cfSBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
2833d2fab5cfSBrian King 
2834d2fab5cfSBrian King 	if (rsp_rc != 0) {
2835d2fab5cfSBrian King 		sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc);
2836d2fab5cfSBrian King 		return -EIO;
2837d2fab5cfSBrian King 	}
2838d2fab5cfSBrian King 
2839d2fab5cfSBrian King 	sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n");
2840d2fab5cfSBrian King 	timeout = wait_for_completion_timeout(&evt->comp, timeout);
2841d2fab5cfSBrian King 
2842d2fab5cfSBrian King 	if (!timeout) {
2843f8804b72SBrian King 		rc = ibmvfc_cancel_all(sdev, 0);
2844d2fab5cfSBrian King 		if (!rc) {
2845d2fab5cfSBrian King 			rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key);
2846d2fab5cfSBrian King 			if (rc == SUCCESS)
2847d2fab5cfSBrian King 				rc = 0;
2848d2fab5cfSBrian King 		}
2849d2fab5cfSBrian King 
2850d2fab5cfSBrian King 		if (rc) {
2851d2fab5cfSBrian King 			sdev_printk(KERN_INFO, sdev, "Cancel failed, resetting host\n");
2852d2fab5cfSBrian King 			ibmvfc_reset_host(vhost);
2853a077c7faSBrian King 			rsp_rc = -EIO;
2854a077c7faSBrian King 			rc = ibmvfc_wait_for_ops(vhost, sdev->hostdata, ibmvfc_match_key);
2855a077c7faSBrian King 
2856a077c7faSBrian King 			if (rc == SUCCESS)
2857d2fab5cfSBrian King 				rsp_rc = 0;
2858a077c7faSBrian King 
2859a077c7faSBrian King 			rc = ibmvfc_wait_for_ops(vhost, evt, ibmvfc_match_evt);
2860a077c7faSBrian King 			if (rc != SUCCESS) {
2861a077c7faSBrian King 				spin_lock_irqsave(vhost->host->host_lock, flags);
2862a077c7faSBrian King 				ibmvfc_hard_reset_host(vhost);
2863a077c7faSBrian King 				spin_unlock_irqrestore(vhost->host->host_lock, flags);
2864a077c7faSBrian King 				rsp_rc = 0;
2865a077c7faSBrian King 			}
2866a077c7faSBrian King 
2867d2fab5cfSBrian King 			goto out;
2868d2fab5cfSBrian King 		}
2869d2fab5cfSBrian King 	}
2870d2fab5cfSBrian King 
2871d2fab5cfSBrian King 	if (rsp_iu.cmd.status)
28725a9d16f7STyrel Datwyler 		rsp_code = ibmvfc_get_err_result(vhost, &rsp_iu.cmd);
2873d2fab5cfSBrian King 
2874d2fab5cfSBrian King 	if (rsp_code) {
2875d2fab5cfSBrian King 		if (fc_rsp->flags & FCP_RSP_LEN_VALID)
2876d2fab5cfSBrian King 			rsp_code = fc_rsp->data.info.rsp_code;
2877d2fab5cfSBrian King 
2878d2fab5cfSBrian King 		sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) "
2879d2fab5cfSBrian King 			    "flags: %x fcp_rsp: %x, scsi_status: %x\n",
28800aab6c3fSTyrel Datwyler 			    ibmvfc_get_cmd_error(be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error)),
28813e6f7de4STyrel Datwyler 			    be16_to_cpu(rsp_iu.cmd.status), be16_to_cpu(rsp_iu.cmd.error), fc_rsp->flags, rsp_code,
2882d2fab5cfSBrian King 			    fc_rsp->scsi_status);
2883d2fab5cfSBrian King 		rsp_rc = -EIO;
2884d2fab5cfSBrian King 	} else
2885d2fab5cfSBrian King 		sdev_printk(KERN_INFO, sdev, "Abort successful\n");
2886d2fab5cfSBrian King 
2887d2fab5cfSBrian King out:
2888d2fab5cfSBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
2889d2fab5cfSBrian King 	ibmvfc_free_event(evt);
2890d2fab5cfSBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
2891d2fab5cfSBrian King 	return rsp_rc;
2892d2fab5cfSBrian King }
2893d2fab5cfSBrian King 
2894d2fab5cfSBrian King /**
2895072b91f9SBrian King  * ibmvfc_eh_abort_handler - Abort a command
2896072b91f9SBrian King  * @cmd:	scsi command to abort
2897072b91f9SBrian King  *
2898072b91f9SBrian King  * Returns:
289993631b4aSBrian King  *	SUCCESS / FAST_IO_FAIL / FAILED
2900072b91f9SBrian King  **/
ibmvfc_eh_abort_handler(struct scsi_cmnd * cmd)2901072b91f9SBrian King static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
2902072b91f9SBrian King {
2903ad8dcffaSBrian King 	struct scsi_device *sdev = cmd->device;
2904ad8dcffaSBrian King 	struct ibmvfc_host *vhost = shost_priv(sdev->host);
290555d29bf0SBrian King 	int cancel_rc, block_rc;
2906ad8dcffaSBrian King 	int rc = FAILED;
2907072b91f9SBrian King 
2908072b91f9SBrian King 	ENTER;
290993631b4aSBrian King 	block_rc = fc_block_scsi_eh(cmd);
2910072b91f9SBrian King 	ibmvfc_wait_while_resetting(vhost);
291193631b4aSBrian King 	if (block_rc != FAST_IO_FAIL) {
2912ad8dcffaSBrian King 		cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
291355d29bf0SBrian King 		ibmvfc_abort_task_set(sdev);
291493631b4aSBrian King 	} else
291590f725dbSBrian King 		cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
2916072b91f9SBrian King 
291755d29bf0SBrian King 	if (!cancel_rc)
2918ad8dcffaSBrian King 		rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
2919072b91f9SBrian King 
292093631b4aSBrian King 	if (block_rc == FAST_IO_FAIL && rc != FAILED)
292193631b4aSBrian King 		rc = FAST_IO_FAIL;
292293631b4aSBrian King 
2923072b91f9SBrian King 	LEAVE;
2924ad8dcffaSBrian King 	return rc;
2925072b91f9SBrian King }
2926072b91f9SBrian King 
2927072b91f9SBrian King /**
2928072b91f9SBrian King  * ibmvfc_eh_device_reset_handler - Reset a single LUN
2929072b91f9SBrian King  * @cmd:	scsi command struct
2930072b91f9SBrian King  *
2931072b91f9SBrian King  * Returns:
293293631b4aSBrian King  *	SUCCESS / FAST_IO_FAIL / FAILED
2933072b91f9SBrian King  **/
ibmvfc_eh_device_reset_handler(struct scsi_cmnd * cmd)2934072b91f9SBrian King static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
2935072b91f9SBrian King {
2936ad8dcffaSBrian King 	struct scsi_device *sdev = cmd->device;
2937ad8dcffaSBrian King 	struct ibmvfc_host *vhost = shost_priv(sdev->host);
293893631b4aSBrian King 	int cancel_rc, block_rc, reset_rc = 0;
2939ad8dcffaSBrian King 	int rc = FAILED;
2940072b91f9SBrian King 
2941072b91f9SBrian King 	ENTER;
294293631b4aSBrian King 	block_rc = fc_block_scsi_eh(cmd);
2943072b91f9SBrian King 	ibmvfc_wait_while_resetting(vhost);
294493631b4aSBrian King 	if (block_rc != FAST_IO_FAIL) {
2945ad8dcffaSBrian King 		cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET);
2946ad8dcffaSBrian King 		reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN");
294793631b4aSBrian King 	} else
294890f725dbSBrian King 		cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
2949072b91f9SBrian King 
2950ad8dcffaSBrian King 	if (!cancel_rc && !reset_rc)
2951ad8dcffaSBrian King 		rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
2952072b91f9SBrian King 
295393631b4aSBrian King 	if (block_rc == FAST_IO_FAIL && rc != FAILED)
295493631b4aSBrian King 		rc = FAST_IO_FAIL;
295593631b4aSBrian King 
2956072b91f9SBrian King 	LEAVE;
2957ad8dcffaSBrian King 	return rc;
2958072b91f9SBrian King }
2959072b91f9SBrian King 
2960072b91f9SBrian King /**
296193631b4aSBrian King  * ibmvfc_dev_cancel_all_noreset - Device iterated cancel all function
296293631b4aSBrian King  * @sdev:	scsi device struct
296393631b4aSBrian King  * @data:	return code
296493631b4aSBrian King  *
296593631b4aSBrian King  **/
ibmvfc_dev_cancel_all_noreset(struct scsi_device * sdev,void * data)296693631b4aSBrian King static void ibmvfc_dev_cancel_all_noreset(struct scsi_device *sdev, void *data)
296793631b4aSBrian King {
296893631b4aSBrian King 	unsigned long *rc = data;
296990f725dbSBrian King 	*rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
297093631b4aSBrian King }
297193631b4aSBrian King 
297293631b4aSBrian King /**
29734a5c4a5eSBrian King  * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function
29744a5c4a5eSBrian King  * @sdev:	scsi device struct
29754a5c4a5eSBrian King  * @data:	return code
29764a5c4a5eSBrian King  *
29774a5c4a5eSBrian King  **/
ibmvfc_dev_cancel_all_reset(struct scsi_device * sdev,void * data)29784a5c4a5eSBrian King static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data)
2979072b91f9SBrian King {
2980072b91f9SBrian King 	unsigned long *rc = data;
2981072b91f9SBrian King 	*rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET);
2982072b91f9SBrian King }
2983072b91f9SBrian King 
2984072b91f9SBrian King /**
2985072b91f9SBrian King  * ibmvfc_eh_target_reset_handler - Reset the target
2986072b91f9SBrian King  * @cmd:	scsi command struct
2987072b91f9SBrian King  *
2988072b91f9SBrian King  * Returns:
298993631b4aSBrian King  *	SUCCESS / FAST_IO_FAIL / FAILED
2990072b91f9SBrian King  **/
ibmvfc_eh_target_reset_handler(struct scsi_cmnd * cmd)2991072b91f9SBrian King static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
2992072b91f9SBrian King {
2993ad8dcffaSBrian King 	struct scsi_device *sdev = cmd->device;
2994ad8dcffaSBrian King 	struct ibmvfc_host *vhost = shost_priv(sdev->host);
2995ad8dcffaSBrian King 	struct scsi_target *starget = scsi_target(sdev);
299693631b4aSBrian King 	int block_rc;
299793631b4aSBrian King 	int reset_rc = 0;
2998ad8dcffaSBrian King 	int rc = FAILED;
2999072b91f9SBrian King 	unsigned long cancel_rc = 0;
3000072b91f9SBrian King 
3001072b91f9SBrian King 	ENTER;
300293631b4aSBrian King 	block_rc = fc_block_scsi_eh(cmd);
3003072b91f9SBrian King 	ibmvfc_wait_while_resetting(vhost);
300493631b4aSBrian King 	if (block_rc != FAST_IO_FAIL) {
30054a5c4a5eSBrian King 		starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_reset);
3006ad8dcffaSBrian King 		reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
300793631b4aSBrian King 	} else
300893631b4aSBrian King 		starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_noreset);
3009072b91f9SBrian King 
3010ad8dcffaSBrian King 	if (!cancel_rc && !reset_rc)
3011ad8dcffaSBrian King 		rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
3012072b91f9SBrian King 
301393631b4aSBrian King 	if (block_rc == FAST_IO_FAIL && rc != FAILED)
301493631b4aSBrian King 		rc = FAST_IO_FAIL;
301593631b4aSBrian King 
3016072b91f9SBrian King 	LEAVE;
3017ad8dcffaSBrian King 	return rc;
3018072b91f9SBrian King }
3019072b91f9SBrian King 
3020072b91f9SBrian King /**
3021072b91f9SBrian King  * ibmvfc_eh_host_reset_handler - Reset the connection to the server
3022072b91f9SBrian King  * @cmd:	struct scsi_cmnd having problems
3023072b91f9SBrian King  *
3024072b91f9SBrian King  **/
ibmvfc_eh_host_reset_handler(struct scsi_cmnd * cmd)3025072b91f9SBrian King static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd)
3026072b91f9SBrian King {
30278d8a3f59SHannes Reinecke 	int rc;
3028072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
3029072b91f9SBrian King 
3030072b91f9SBrian King 	dev_err(vhost->dev, "Resetting connection due to error recovery\n");
3031072b91f9SBrian King 	rc = ibmvfc_issue_fc_host_lip(vhost->host);
303293631b4aSBrian King 
3033072b91f9SBrian King 	return rc ? FAILED : SUCCESS;
3034072b91f9SBrian King }
3035072b91f9SBrian King 
3036072b91f9SBrian King /**
3037072b91f9SBrian King  * ibmvfc_terminate_rport_io - Terminate all pending I/O to the rport.
3038072b91f9SBrian King  * @rport:		rport struct
3039072b91f9SBrian King  *
3040072b91f9SBrian King  * Return value:
3041072b91f9SBrian King  * 	none
3042072b91f9SBrian King  **/
ibmvfc_terminate_rport_io(struct fc_rport * rport)3043072b91f9SBrian King static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
3044072b91f9SBrian King {
3045d2fab5cfSBrian King 	struct Scsi_Host *shost = rport_to_shost(rport);
3046072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
3047d2fab5cfSBrian King 	struct fc_rport *dev_rport;
3048d2fab5cfSBrian King 	struct scsi_device *sdev;
30494b29cb61SBrian King 	struct ibmvfc_target *tgt;
30504b29cb61SBrian King 	unsigned long rc, flags;
30514b29cb61SBrian King 	unsigned int found;
3052072b91f9SBrian King 
3053072b91f9SBrian King 	ENTER;
3054d2fab5cfSBrian King 	shost_for_each_device(sdev, shost) {
3055d2fab5cfSBrian King 		dev_rport = starget_to_rport(scsi_target(sdev));
3056d2fab5cfSBrian King 		if (dev_rport != rport)
3057d2fab5cfSBrian King 			continue;
305890f725dbSBrian King 		ibmvfc_cancel_all(sdev, IBMVFC_TMF_SUPPRESS_ABTS);
3059d2fab5cfSBrian King 	}
3060072b91f9SBrian King 
3061d2fab5cfSBrian King 	rc = ibmvfc_wait_for_ops(vhost, rport, ibmvfc_match_rport);
3062ad8dcffaSBrian King 
3063ad8dcffaSBrian King 	if (rc == FAILED)
3064072b91f9SBrian King 		ibmvfc_issue_fc_host_lip(shost);
30654b29cb61SBrian King 
30664b29cb61SBrian King 	spin_lock_irqsave(shost->host_lock, flags);
30674b29cb61SBrian King 	found = 0;
30684b29cb61SBrian King 	list_for_each_entry(tgt, &vhost->targets, queue) {
30694b29cb61SBrian King 		if (tgt->scsi_id == rport->port_id) {
30704b29cb61SBrian King 			found++;
30714b29cb61SBrian King 			break;
30724b29cb61SBrian King 		}
30734b29cb61SBrian King 	}
30744b29cb61SBrian King 
30754b29cb61SBrian King 	if (found && tgt->action == IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT) {
30764b29cb61SBrian King 		/*
30774b29cb61SBrian King 		 * If we get here, that means we previously attempted to send
30784b29cb61SBrian King 		 * an implicit logout to the target but it failed, most likely
30794b29cb61SBrian King 		 * due to I/O being pending, so we need to send it again
30804b29cb61SBrian King 		 */
30814b29cb61SBrian King 		ibmvfc_del_tgt(tgt);
30824b29cb61SBrian King 		ibmvfc_reinit_host(vhost);
30834b29cb61SBrian King 	}
30844b29cb61SBrian King 
30854b29cb61SBrian King 	spin_unlock_irqrestore(shost->host_lock, flags);
3086072b91f9SBrian King 	LEAVE;
3087072b91f9SBrian King }
3088072b91f9SBrian King 
3089d99e5f48SBrian King static const struct ibmvfc_async_desc ae_desc [] = {
3090402c6eecSRobert Jennings 	{ "PLOGI",	IBMVFC_AE_ELS_PLOGI,	IBMVFC_DEFAULT_LOG_LEVEL + 1 },
3091402c6eecSRobert Jennings 	{ "LOGO",	IBMVFC_AE_ELS_LOGO,	IBMVFC_DEFAULT_LOG_LEVEL + 1 },
3092402c6eecSRobert Jennings 	{ "PRLO",	IBMVFC_AE_ELS_PRLO,	IBMVFC_DEFAULT_LOG_LEVEL + 1 },
3093402c6eecSRobert Jennings 	{ "N-Port SCN",	IBMVFC_AE_SCN_NPORT,	IBMVFC_DEFAULT_LOG_LEVEL + 1 },
3094402c6eecSRobert Jennings 	{ "Group SCN",	IBMVFC_AE_SCN_GROUP,	IBMVFC_DEFAULT_LOG_LEVEL + 1 },
3095402c6eecSRobert Jennings 	{ "Domain SCN",	IBMVFC_AE_SCN_DOMAIN,	IBMVFC_DEFAULT_LOG_LEVEL },
3096402c6eecSRobert Jennings 	{ "Fabric SCN",	IBMVFC_AE_SCN_FABRIC,	IBMVFC_DEFAULT_LOG_LEVEL },
3097402c6eecSRobert Jennings 	{ "Link Up",	IBMVFC_AE_LINK_UP,	IBMVFC_DEFAULT_LOG_LEVEL },
3098402c6eecSRobert Jennings 	{ "Link Down",	IBMVFC_AE_LINK_DOWN,	IBMVFC_DEFAULT_LOG_LEVEL },
3099402c6eecSRobert Jennings 	{ "Link Dead",	IBMVFC_AE_LINK_DEAD,	IBMVFC_DEFAULT_LOG_LEVEL },
3100402c6eecSRobert Jennings 	{ "Halt",	IBMVFC_AE_HALT,		IBMVFC_DEFAULT_LOG_LEVEL },
3101402c6eecSRobert Jennings 	{ "Resume",	IBMVFC_AE_RESUME,	IBMVFC_DEFAULT_LOG_LEVEL },
3102402c6eecSRobert Jennings 	{ "Adapter Failed", IBMVFC_AE_ADAPTER_FAILED, IBMVFC_DEFAULT_LOG_LEVEL },
3103072b91f9SBrian King };
3104072b91f9SBrian King 
3105d99e5f48SBrian King static const struct ibmvfc_async_desc unknown_ae = {
3106402c6eecSRobert Jennings 	"Unknown async", 0, IBMVFC_DEFAULT_LOG_LEVEL
3107d99e5f48SBrian King };
3108072b91f9SBrian King 
3109072b91f9SBrian King /**
3110072b91f9SBrian King  * ibmvfc_get_ae_desc - Get text description for async event
3111072b91f9SBrian King  * @ae:	async event
3112072b91f9SBrian King  *
3113072b91f9SBrian King  **/
ibmvfc_get_ae_desc(u64 ae)3114d99e5f48SBrian King static const struct ibmvfc_async_desc *ibmvfc_get_ae_desc(u64 ae)
3115072b91f9SBrian King {
3116072b91f9SBrian King 	int i;
3117072b91f9SBrian King 
3118072b91f9SBrian King 	for (i = 0; i < ARRAY_SIZE(ae_desc); i++)
3119072b91f9SBrian King 		if (ae_desc[i].ae == ae)
3120d99e5f48SBrian King 			return &ae_desc[i];
3121072b91f9SBrian King 
3122d99e5f48SBrian King 	return &unknown_ae;
3123d99e5f48SBrian King }
3124d99e5f48SBrian King 
3125d99e5f48SBrian King static const struct {
3126d99e5f48SBrian King 	enum ibmvfc_ae_link_state state;
3127d99e5f48SBrian King 	const char *desc;
3128d99e5f48SBrian King } link_desc [] = {
3129d99e5f48SBrian King 	{ IBMVFC_AE_LS_LINK_UP,		" link up" },
3130d99e5f48SBrian King 	{ IBMVFC_AE_LS_LINK_BOUNCED,	" link bounced" },
3131d99e5f48SBrian King 	{ IBMVFC_AE_LS_LINK_DOWN,	" link down" },
3132d99e5f48SBrian King 	{ IBMVFC_AE_LS_LINK_DEAD,	" link dead" },
3133d99e5f48SBrian King };
3134d99e5f48SBrian King 
3135d99e5f48SBrian King /**
3136d99e5f48SBrian King  * ibmvfc_get_link_state - Get text description for link state
3137d99e5f48SBrian King  * @state:	link state
3138d99e5f48SBrian King  *
3139d99e5f48SBrian King  **/
ibmvfc_get_link_state(enum ibmvfc_ae_link_state state)3140d99e5f48SBrian King static const char *ibmvfc_get_link_state(enum ibmvfc_ae_link_state state)
3141d99e5f48SBrian King {
3142d99e5f48SBrian King 	int i;
3143d99e5f48SBrian King 
3144d99e5f48SBrian King 	for (i = 0; i < ARRAY_SIZE(link_desc); i++)
3145d99e5f48SBrian King 		if (link_desc[i].state == state)
3146d99e5f48SBrian King 			return link_desc[i].desc;
3147d99e5f48SBrian King 
3148d99e5f48SBrian King 	return "";
3149072b91f9SBrian King }
3150072b91f9SBrian King 
3151072b91f9SBrian King /**
3152072b91f9SBrian King  * ibmvfc_handle_async - Handle an async event from the adapter
3153072b91f9SBrian King  * @crq:	crq to process
3154072b91f9SBrian King  * @vhost:	ibmvfc host struct
3155072b91f9SBrian King  *
3156072b91f9SBrian King  **/
ibmvfc_handle_async(struct ibmvfc_async_crq * crq,struct ibmvfc_host * vhost)3157072b91f9SBrian King static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
3158072b91f9SBrian King 				struct ibmvfc_host *vhost)
3159072b91f9SBrian King {
31600aab6c3fSTyrel Datwyler 	const struct ibmvfc_async_desc *desc = ibmvfc_get_ae_desc(be64_to_cpu(crq->event));
31616d29cc56SBrian King 	struct ibmvfc_target *tgt;
3162072b91f9SBrian King 
3163d99e5f48SBrian King 	ibmvfc_log(vhost, desc->log_level, "%s event received. scsi_id: %llx, wwpn: %llx,"
316421367e20STyrel Datwyler 		   " node_name: %llx%s\n", desc->desc, be64_to_cpu(crq->scsi_id),
316521367e20STyrel Datwyler 		   be64_to_cpu(crq->wwpn), be64_to_cpu(crq->node_name),
3166d99e5f48SBrian King 		   ibmvfc_get_link_state(crq->link_state));
3167072b91f9SBrian King 
31680aab6c3fSTyrel Datwyler 	switch (be64_to_cpu(crq->event)) {
3169072b91f9SBrian King 	case IBMVFC_AE_RESUME:
3170497f9c50SBrian King 		switch (crq->link_state) {
3171497f9c50SBrian King 		case IBMVFC_AE_LS_LINK_DOWN:
3172497f9c50SBrian King 			ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
3173497f9c50SBrian King 			break;
3174497f9c50SBrian King 		case IBMVFC_AE_LS_LINK_DEAD:
3175497f9c50SBrian King 			ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
3176497f9c50SBrian King 			break;
3177497f9c50SBrian King 		case IBMVFC_AE_LS_LINK_UP:
3178497f9c50SBrian King 		case IBMVFC_AE_LS_LINK_BOUNCED:
3179497f9c50SBrian King 		default:
3180497f9c50SBrian King 			vhost->events_to_log |= IBMVFC_AE_LINKUP;
3181497f9c50SBrian King 			vhost->delay_init = 1;
3182497f9c50SBrian King 			__ibmvfc_reset_host(vhost);
3183497f9c50SBrian King 			break;
3184f36cfe6aSChristopher Díaz Riveros 		}
3185497f9c50SBrian King 
3186497f9c50SBrian King 		break;
3187497f9c50SBrian King 	case IBMVFC_AE_LINK_UP:
3188072b91f9SBrian King 		vhost->events_to_log |= IBMVFC_AE_LINKUP;
3189d2131b33SBrian King 		vhost->delay_init = 1;
3190d2131b33SBrian King 		__ibmvfc_reset_host(vhost);
3191072b91f9SBrian King 		break;
3192072b91f9SBrian King 	case IBMVFC_AE_SCN_FABRIC:
3193d2131b33SBrian King 	case IBMVFC_AE_SCN_DOMAIN:
3194072b91f9SBrian King 		vhost->events_to_log |= IBMVFC_AE_RSCN;
31955cdf1626SBrian King 		if (vhost->state < IBMVFC_HALTED) {
3196d2131b33SBrian King 			vhost->delay_init = 1;
3197d2131b33SBrian King 			__ibmvfc_reset_host(vhost);
31985cdf1626SBrian King 		}
3199072b91f9SBrian King 		break;
3200072b91f9SBrian King 	case IBMVFC_AE_SCN_NPORT:
3201072b91f9SBrian King 	case IBMVFC_AE_SCN_GROUP:
3202072b91f9SBrian King 		vhost->events_to_log |= IBMVFC_AE_RSCN;
32036d29cc56SBrian King 		ibmvfc_reinit_host(vhost);
32046d29cc56SBrian King 		break;
3205072b91f9SBrian King 	case IBMVFC_AE_ELS_LOGO:
3206072b91f9SBrian King 	case IBMVFC_AE_ELS_PRLO:
3207072b91f9SBrian King 	case IBMVFC_AE_ELS_PLOGI:
32086d29cc56SBrian King 		list_for_each_entry(tgt, &vhost->targets, queue) {
32096d29cc56SBrian King 			if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
32106d29cc56SBrian King 				break;
32110aab6c3fSTyrel Datwyler 			if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id)
32126d29cc56SBrian King 				continue;
32130aab6c3fSTyrel Datwyler 			if (crq->wwpn && cpu_to_be64(tgt->ids.port_name) != crq->wwpn)
32146d29cc56SBrian King 				continue;
32150aab6c3fSTyrel Datwyler 			if (crq->node_name && cpu_to_be64(tgt->ids.node_name) != crq->node_name)
32166d29cc56SBrian King 				continue;
32170aab6c3fSTyrel Datwyler 			if (tgt->need_login && be64_to_cpu(crq->event) == IBMVFC_AE_ELS_LOGO)
3218017b2ae3SBrian King 				tgt->logo_rcvd = 1;
32190aab6c3fSTyrel Datwyler 			if (!tgt->need_login || be64_to_cpu(crq->event) == IBMVFC_AE_ELS_PLOGI) {
3220ed830385SBrian King 				ibmvfc_del_tgt(tgt);
3221072b91f9SBrian King 				ibmvfc_reinit_host(vhost);
3222017b2ae3SBrian King 			}
3223017b2ae3SBrian King 		}
3224072b91f9SBrian King 		break;
3225072b91f9SBrian King 	case IBMVFC_AE_LINK_DOWN:
3226072b91f9SBrian King 	case IBMVFC_AE_ADAPTER_FAILED:
3227072b91f9SBrian King 		ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
3228072b91f9SBrian King 		break;
3229072b91f9SBrian King 	case IBMVFC_AE_LINK_DEAD:
3230072b91f9SBrian King 		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
3231072b91f9SBrian King 		break;
3232072b91f9SBrian King 	case IBMVFC_AE_HALT:
3233072b91f9SBrian King 		ibmvfc_link_down(vhost, IBMVFC_HALTED);
3234072b91f9SBrian King 		break;
3235072b91f9SBrian King 	default:
3236775a42ecSStephen Rothwell 		dev_err(vhost->dev, "Unknown async event received: %lld\n", crq->event);
3237072b91f9SBrian King 		break;
3238f36cfe6aSChristopher Díaz Riveros 	}
3239072b91f9SBrian King }
3240072b91f9SBrian King 
3241072b91f9SBrian King /**
3242072b91f9SBrian King  * ibmvfc_handle_crq - Handles and frees received events in the CRQ
3243072b91f9SBrian King  * @crq:	Command/Response queue
3244072b91f9SBrian King  * @vhost:	ibmvfc host struct
3245dd9c7729SLee Jones  * @evt_doneq:	Event done queue
3246072b91f9SBrian King  *
3247072b91f9SBrian King **/
ibmvfc_handle_crq(struct ibmvfc_crq * crq,struct ibmvfc_host * vhost,struct list_head * evt_doneq)32481f4a4a19STyrel Datwyler static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost,
32491f4a4a19STyrel Datwyler 			      struct list_head *evt_doneq)
3250072b91f9SBrian King {
3251072b91f9SBrian King 	long rc;
32520aab6c3fSTyrel Datwyler 	struct ibmvfc_event *evt = (struct ibmvfc_event *)be64_to_cpu(crq->ioba);
3253072b91f9SBrian King 
3254072b91f9SBrian King 	switch (crq->valid) {
3255072b91f9SBrian King 	case IBMVFC_CRQ_INIT_RSP:
3256072b91f9SBrian King 		switch (crq->format) {
3257072b91f9SBrian King 		case IBMVFC_CRQ_INIT:
3258072b91f9SBrian King 			dev_info(vhost->dev, "Partner initialized\n");
3259072b91f9SBrian King 			/* Send back a response */
3260072b91f9SBrian King 			rc = ibmvfc_send_crq_init_complete(vhost);
3261072b91f9SBrian King 			if (rc == 0)
3262861890c6SBrian King 				ibmvfc_init_host(vhost);
3263072b91f9SBrian King 			else
3264072b91f9SBrian King 				dev_err(vhost->dev, "Unable to send init rsp. rc=%ld\n", rc);
3265072b91f9SBrian King 			break;
3266072b91f9SBrian King 		case IBMVFC_CRQ_INIT_COMPLETE:
3267072b91f9SBrian King 			dev_info(vhost->dev, "Partner initialization complete\n");
3268861890c6SBrian King 			ibmvfc_init_host(vhost);
3269072b91f9SBrian King 			break;
3270072b91f9SBrian King 		default:
3271072b91f9SBrian King 			dev_err(vhost->dev, "Unknown crq message type: %d\n", crq->format);
3272072b91f9SBrian King 		}
3273072b91f9SBrian King 		return;
3274072b91f9SBrian King 	case IBMVFC_CRQ_XPORT_EVENT:
3275072b91f9SBrian King 		vhost->state = IBMVFC_NO_CRQ;
327679111d08SBrian King 		vhost->logged_in = 0;
3277072b91f9SBrian King 		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
3278072b91f9SBrian King 		if (crq->format == IBMVFC_PARTITION_MIGRATED) {
3279072b91f9SBrian King 			/* We need to re-setup the interpartition connection */
3280d6e2635bSTyrel Datwyler 			dev_info(vhost->dev, "Partition migrated, Re-enabling adapter\n");
3281072b91f9SBrian King 			vhost->client_migrated = 1;
328262fa3ce0SBrian King 
328362fa3ce0SBrian King 			scsi_block_requests(vhost->host);
3284072b91f9SBrian King 			ibmvfc_purge_requests(vhost, DID_REQUEUE);
328562fa3ce0SBrian King 			ibmvfc_set_host_state(vhost, IBMVFC_LINK_DOWN);
328673ee5d86SBrian King 			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_REENABLE);
328762fa3ce0SBrian King 			wake_up(&vhost->work_wait_q);
3288d6e2635bSTyrel Datwyler 		} else if (crq->format == IBMVFC_PARTNER_FAILED || crq->format == IBMVFC_PARTNER_DEREGISTER) {
3289d6e2635bSTyrel Datwyler 			dev_err(vhost->dev, "Host partner adapter deregistered or failed (rc=%d)\n", crq->format);
3290072b91f9SBrian King 			ibmvfc_purge_requests(vhost, DID_ERROR);
3291072b91f9SBrian King 			ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
329273ee5d86SBrian King 			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET);
3293d6e2635bSTyrel Datwyler 		} else {
3294d6e2635bSTyrel Datwyler 			dev_err(vhost->dev, "Received unknown transport event from partner (rc=%d)\n", crq->format);
3295072b91f9SBrian King 		}
3296072b91f9SBrian King 		return;
3297072b91f9SBrian King 	case IBMVFC_CRQ_CMD_RSP:
3298072b91f9SBrian King 		break;
3299072b91f9SBrian King 	default:
3300072b91f9SBrian King 		dev_err(vhost->dev, "Got an invalid message type 0x%02x\n", crq->valid);
3301072b91f9SBrian King 		return;
3302072b91f9SBrian King 	}
3303072b91f9SBrian King 
3304072b91f9SBrian King 	if (crq->format == IBMVFC_ASYNC_EVENT)
3305072b91f9SBrian King 		return;
3306072b91f9SBrian King 
3307072b91f9SBrian King 	/* The only kind of payload CRQs we should get are responses to
3308072b91f9SBrian King 	 * things we send. Make sure this response is to something we
3309072b91f9SBrian King 	 * actually sent
3310072b91f9SBrian King 	 */
3311e4b26f3dSTyrel Datwyler 	if (unlikely(!ibmvfc_valid_event(&vhost->crq.evt_pool, evt))) {
3312775a42ecSStephen Rothwell 		dev_err(vhost->dev, "Returned correlation_token 0x%08llx is invalid!\n",
3313072b91f9SBrian King 			crq->ioba);
3314072b91f9SBrian King 		return;
3315072b91f9SBrian King 	}
3316072b91f9SBrian King 
3317a264cf5eSTyrel Datwyler 	if (unlikely(atomic_dec_if_positive(&evt->active))) {
3318775a42ecSStephen Rothwell 		dev_err(vhost->dev, "Received duplicate correlation_token 0x%08llx!\n",
3319072b91f9SBrian King 			crq->ioba);
3320072b91f9SBrian King 		return;
3321072b91f9SBrian King 	}
3322072b91f9SBrian King 
332357e80e0bSTyrel Datwyler 	spin_lock(&evt->queue->l_lock);
33241f4a4a19STyrel Datwyler 	list_move_tail(&evt->queue_list, evt_doneq);
332557e80e0bSTyrel Datwyler 	spin_unlock(&evt->queue->l_lock);
3326072b91f9SBrian King }
3327072b91f9SBrian King 
3328072b91f9SBrian King /**
3329072b91f9SBrian King  * ibmvfc_scan_finished - Check if the device scan is done.
3330072b91f9SBrian King  * @shost:	scsi host struct
3331072b91f9SBrian King  * @time:	current elapsed time
3332072b91f9SBrian King  *
3333072b91f9SBrian King  * Returns:
3334072b91f9SBrian King  *	0 if scan is not done / 1 if scan is done
3335072b91f9SBrian King  **/
ibmvfc_scan_finished(struct Scsi_Host * shost,unsigned long time)3336072b91f9SBrian King static int ibmvfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
3337072b91f9SBrian King {
3338072b91f9SBrian King 	unsigned long flags;
3339072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
3340072b91f9SBrian King 	int done = 0;
3341072b91f9SBrian King 
3342072b91f9SBrian King 	spin_lock_irqsave(shost->host_lock, flags);
33437a3795f2SHannes Reinecke 	if (!vhost->scan_timeout)
33447a3795f2SHannes Reinecke 		done = 1;
33457a3795f2SHannes Reinecke 	else if (time >= (vhost->scan_timeout * HZ)) {
3346072b91f9SBrian King 		dev_info(vhost->dev, "Scan taking longer than %d seconds, "
33477a3795f2SHannes Reinecke 			 "continuing initialization\n", vhost->scan_timeout);
3348072b91f9SBrian King 		done = 1;
3349072b91f9SBrian King 	}
3350072b91f9SBrian King 
33517a3795f2SHannes Reinecke 	if (vhost->scan_complete) {
33527a3795f2SHannes Reinecke 		vhost->scan_timeout = init_timeout;
3353072b91f9SBrian King 		done = 1;
33547a3795f2SHannes Reinecke 	}
3355072b91f9SBrian King 	spin_unlock_irqrestore(shost->host_lock, flags);
3356072b91f9SBrian King 	return done;
3357072b91f9SBrian King }
3358072b91f9SBrian King 
3359072b91f9SBrian King /**
3360072b91f9SBrian King  * ibmvfc_slave_alloc - Setup the device's task set value
3361072b91f9SBrian King  * @sdev:	struct scsi_device device to configure
3362072b91f9SBrian King  *
3363072b91f9SBrian King  * Set the device's task set value so that error handling works as
3364072b91f9SBrian King  * expected.
3365072b91f9SBrian King  *
3366072b91f9SBrian King  * Returns:
3367072b91f9SBrian King  *	0 on success / -ENXIO if device does not exist
3368072b91f9SBrian King  **/
ibmvfc_slave_alloc(struct scsi_device * sdev)3369072b91f9SBrian King static int ibmvfc_slave_alloc(struct scsi_device *sdev)
3370072b91f9SBrian King {
3371072b91f9SBrian King 	struct Scsi_Host *shost = sdev->host;
3372072b91f9SBrian King 	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
3373072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
3374072b91f9SBrian King 	unsigned long flags = 0;
3375072b91f9SBrian King 
3376072b91f9SBrian King 	if (!rport || fc_remote_port_chkready(rport))
3377072b91f9SBrian King 		return -ENXIO;
3378072b91f9SBrian King 
3379072b91f9SBrian King 	spin_lock_irqsave(shost->host_lock, flags);
3380072b91f9SBrian King 	sdev->hostdata = (void *)(unsigned long)vhost->task_set++;
3381072b91f9SBrian King 	spin_unlock_irqrestore(shost->host_lock, flags);
3382072b91f9SBrian King 	return 0;
3383072b91f9SBrian King }
3384072b91f9SBrian King 
3385072b91f9SBrian King /**
3386ad8dcffaSBrian King  * ibmvfc_target_alloc - Setup the target's task set value
3387ad8dcffaSBrian King  * @starget:	struct scsi_target
3388ad8dcffaSBrian King  *
3389ad8dcffaSBrian King  * Set the target's task set value so that error handling works as
3390ad8dcffaSBrian King  * expected.
3391ad8dcffaSBrian King  *
3392ad8dcffaSBrian King  * Returns:
3393ad8dcffaSBrian King  *	0 on success / -ENXIO if device does not exist
3394ad8dcffaSBrian King  **/
ibmvfc_target_alloc(struct scsi_target * starget)3395ad8dcffaSBrian King static int ibmvfc_target_alloc(struct scsi_target *starget)
3396ad8dcffaSBrian King {
3397ad8dcffaSBrian King 	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
3398ad8dcffaSBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
3399ad8dcffaSBrian King 	unsigned long flags = 0;
3400ad8dcffaSBrian King 
3401ad8dcffaSBrian King 	spin_lock_irqsave(shost->host_lock, flags);
3402ad8dcffaSBrian King 	starget->hostdata = (void *)(unsigned long)vhost->task_set++;
3403ad8dcffaSBrian King 	spin_unlock_irqrestore(shost->host_lock, flags);
3404ad8dcffaSBrian King 	return 0;
3405ad8dcffaSBrian King }
3406ad8dcffaSBrian King 
3407ad8dcffaSBrian King /**
3408072b91f9SBrian King  * ibmvfc_slave_configure - Configure the device
3409072b91f9SBrian King  * @sdev:	struct scsi_device device to configure
3410072b91f9SBrian King  *
3411072b91f9SBrian King  * Enable allow_restart for a device if it is a disk. Adjust the
3412072b91f9SBrian King  * queue_depth here also.
3413072b91f9SBrian King  *
3414072b91f9SBrian King  * Returns:
3415072b91f9SBrian King  *	0
3416072b91f9SBrian King  **/
ibmvfc_slave_configure(struct scsi_device * sdev)3417072b91f9SBrian King static int ibmvfc_slave_configure(struct scsi_device *sdev)
3418072b91f9SBrian King {
3419072b91f9SBrian King 	struct Scsi_Host *shost = sdev->host;
3420072b91f9SBrian King 	unsigned long flags = 0;
3421072b91f9SBrian King 
3422072b91f9SBrian King 	spin_lock_irqsave(shost->host_lock, flags);
342376490729SBrian King 	if (sdev->type == TYPE_DISK) {
3424072b91f9SBrian King 		sdev->allow_restart = 1;
342576490729SBrian King 		blk_queue_rq_timeout(sdev->request_queue, 120 * HZ);
342676490729SBrian King 	}
3427072b91f9SBrian King 	spin_unlock_irqrestore(shost->host_lock, flags);
3428072b91f9SBrian King 	return 0;
3429072b91f9SBrian King }
3430072b91f9SBrian King 
3431072b91f9SBrian King /**
3432072b91f9SBrian King  * ibmvfc_change_queue_depth - Change the device's queue depth
3433072b91f9SBrian King  * @sdev:	scsi device struct
3434072b91f9SBrian King  * @qdepth:	depth to set
3435072b91f9SBrian King  *
3436072b91f9SBrian King  * Return value:
3437072b91f9SBrian King  * 	actual depth set
3438072b91f9SBrian King  **/
ibmvfc_change_queue_depth(struct scsi_device * sdev,int qdepth)3439db5ed4dfSChristoph Hellwig static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth)
3440072b91f9SBrian King {
3441072b91f9SBrian King 	if (qdepth > IBMVFC_MAX_CMDS_PER_LUN)
3442072b91f9SBrian King 		qdepth = IBMVFC_MAX_CMDS_PER_LUN;
3443072b91f9SBrian King 
3444db5ed4dfSChristoph Hellwig 	return scsi_change_queue_depth(sdev, qdepth);
3445072b91f9SBrian King }
3446072b91f9SBrian King 
ibmvfc_show_host_partition_name(struct device * dev,struct device_attribute * attr,char * buf)3447072b91f9SBrian King static ssize_t ibmvfc_show_host_partition_name(struct device *dev,
3448072b91f9SBrian King 						 struct device_attribute *attr, char *buf)
3449072b91f9SBrian King {
3450072b91f9SBrian King 	struct Scsi_Host *shost = class_to_shost(dev);
3451072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
3452072b91f9SBrian King 
3453072b91f9SBrian King 	return snprintf(buf, PAGE_SIZE, "%s\n",
3454072b91f9SBrian King 			vhost->login_buf->resp.partition_name);
3455072b91f9SBrian King }
3456072b91f9SBrian King 
ibmvfc_show_host_device_name(struct device * dev,struct device_attribute * attr,char * buf)3457072b91f9SBrian King static ssize_t ibmvfc_show_host_device_name(struct device *dev,
3458072b91f9SBrian King 					    struct device_attribute *attr, char *buf)
3459072b91f9SBrian King {
3460072b91f9SBrian King 	struct Scsi_Host *shost = class_to_shost(dev);
3461072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
3462072b91f9SBrian King 
3463072b91f9SBrian King 	return snprintf(buf, PAGE_SIZE, "%s\n",
3464072b91f9SBrian King 			vhost->login_buf->resp.device_name);
3465072b91f9SBrian King }
3466072b91f9SBrian King 
ibmvfc_show_host_loc_code(struct device * dev,struct device_attribute * attr,char * buf)3467072b91f9SBrian King static ssize_t ibmvfc_show_host_loc_code(struct device *dev,
3468072b91f9SBrian King 					 struct device_attribute *attr, char *buf)
3469072b91f9SBrian King {
3470072b91f9SBrian King 	struct Scsi_Host *shost = class_to_shost(dev);
3471072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
3472072b91f9SBrian King 
3473072b91f9SBrian King 	return snprintf(buf, PAGE_SIZE, "%s\n",
3474072b91f9SBrian King 			vhost->login_buf->resp.port_loc_code);
3475072b91f9SBrian King }
3476072b91f9SBrian King 
ibmvfc_show_host_drc_name(struct device * dev,struct device_attribute * attr,char * buf)3477072b91f9SBrian King static ssize_t ibmvfc_show_host_drc_name(struct device *dev,
3478072b91f9SBrian King 					 struct device_attribute *attr, char *buf)
3479072b91f9SBrian King {
3480072b91f9SBrian King 	struct Scsi_Host *shost = class_to_shost(dev);
3481072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
3482072b91f9SBrian King 
3483072b91f9SBrian King 	return snprintf(buf, PAGE_SIZE, "%s\n",
3484072b91f9SBrian King 			vhost->login_buf->resp.drc_name);
3485072b91f9SBrian King }
3486072b91f9SBrian King 
ibmvfc_show_host_npiv_version(struct device * dev,struct device_attribute * attr,char * buf)3487072b91f9SBrian King static ssize_t ibmvfc_show_host_npiv_version(struct device *dev,
3488072b91f9SBrian King 					     struct device_attribute *attr, char *buf)
3489072b91f9SBrian King {
3490072b91f9SBrian King 	struct Scsi_Host *shost = class_to_shost(dev);
3491072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
349261bdb4eeSTyrel Datwyler 	return snprintf(buf, PAGE_SIZE, "%d\n", be32_to_cpu(vhost->login_buf->resp.version));
3493072b91f9SBrian King }
3494072b91f9SBrian King 
ibmvfc_show_host_capabilities(struct device * dev,struct device_attribute * attr,char * buf)3495497f9c50SBrian King static ssize_t ibmvfc_show_host_capabilities(struct device *dev,
3496497f9c50SBrian King 					     struct device_attribute *attr, char *buf)
3497497f9c50SBrian King {
3498497f9c50SBrian King 	struct Scsi_Host *shost = class_to_shost(dev);
3499497f9c50SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
350061bdb4eeSTyrel Datwyler 	return snprintf(buf, PAGE_SIZE, "%llx\n", be64_to_cpu(vhost->login_buf->resp.capabilities));
3501497f9c50SBrian King }
3502497f9c50SBrian King 
3503072b91f9SBrian King /**
3504072b91f9SBrian King  * ibmvfc_show_log_level - Show the adapter's error logging level
3505072b91f9SBrian King  * @dev:	class device struct
3506dd9c7729SLee Jones  * @attr:	unused
3507072b91f9SBrian King  * @buf:	buffer
3508072b91f9SBrian King  *
3509072b91f9SBrian King  * Return value:
3510072b91f9SBrian King  * 	number of bytes printed to buffer
3511072b91f9SBrian King  **/
ibmvfc_show_log_level(struct device * dev,struct device_attribute * attr,char * buf)3512072b91f9SBrian King static ssize_t ibmvfc_show_log_level(struct device *dev,
3513072b91f9SBrian King 				     struct device_attribute *attr, char *buf)
3514072b91f9SBrian King {
3515072b91f9SBrian King 	struct Scsi_Host *shost = class_to_shost(dev);
3516072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
3517072b91f9SBrian King 	unsigned long flags = 0;
3518072b91f9SBrian King 	int len;
3519072b91f9SBrian King 
3520072b91f9SBrian King 	spin_lock_irqsave(shost->host_lock, flags);
3521072b91f9SBrian King 	len = snprintf(buf, PAGE_SIZE, "%d\n", vhost->log_level);
3522072b91f9SBrian King 	spin_unlock_irqrestore(shost->host_lock, flags);
3523072b91f9SBrian King 	return len;
3524072b91f9SBrian King }
3525072b91f9SBrian King 
3526072b91f9SBrian King /**
3527072b91f9SBrian King  * ibmvfc_store_log_level - Change the adapter's error logging level
3528072b91f9SBrian King  * @dev:	class device struct
3529dd9c7729SLee Jones  * @attr:	unused
3530072b91f9SBrian King  * @buf:	buffer
3531dd9c7729SLee Jones  * @count:      buffer size
3532072b91f9SBrian King  *
3533072b91f9SBrian King  * Return value:
3534072b91f9SBrian King  * 	number of bytes printed to buffer
3535072b91f9SBrian King  **/
ibmvfc_store_log_level(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)3536072b91f9SBrian King static ssize_t ibmvfc_store_log_level(struct device *dev,
3537072b91f9SBrian King 				      struct device_attribute *attr,
3538072b91f9SBrian King 				      const char *buf, size_t count)
3539072b91f9SBrian King {
3540072b91f9SBrian King 	struct Scsi_Host *shost = class_to_shost(dev);
3541072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
3542072b91f9SBrian King 	unsigned long flags = 0;
3543072b91f9SBrian King 
3544072b91f9SBrian King 	spin_lock_irqsave(shost->host_lock, flags);
3545072b91f9SBrian King 	vhost->log_level = simple_strtoul(buf, NULL, 10);
3546072b91f9SBrian King 	spin_unlock_irqrestore(shost->host_lock, flags);
3547072b91f9SBrian King 	return strlen(buf);
3548072b91f9SBrian King }
3549072b91f9SBrian King 
ibmvfc_show_scsi_channels(struct device * dev,struct device_attribute * attr,char * buf)3550032d1900STyrel Datwyler static ssize_t ibmvfc_show_scsi_channels(struct device *dev,
3551032d1900STyrel Datwyler 					 struct device_attribute *attr, char *buf)
3552032d1900STyrel Datwyler {
3553032d1900STyrel Datwyler 	struct Scsi_Host *shost = class_to_shost(dev);
3554032d1900STyrel Datwyler 	struct ibmvfc_host *vhost = shost_priv(shost);
3555032d1900STyrel Datwyler 	unsigned long flags = 0;
3556032d1900STyrel Datwyler 	int len;
3557032d1900STyrel Datwyler 
3558032d1900STyrel Datwyler 	spin_lock_irqsave(shost->host_lock, flags);
3559032d1900STyrel Datwyler 	len = snprintf(buf, PAGE_SIZE, "%d\n", vhost->client_scsi_channels);
3560032d1900STyrel Datwyler 	spin_unlock_irqrestore(shost->host_lock, flags);
3561032d1900STyrel Datwyler 	return len;
3562032d1900STyrel Datwyler }
3563032d1900STyrel Datwyler 
ibmvfc_store_scsi_channels(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)3564032d1900STyrel Datwyler static ssize_t ibmvfc_store_scsi_channels(struct device *dev,
3565032d1900STyrel Datwyler 					 struct device_attribute *attr,
3566032d1900STyrel Datwyler 					 const char *buf, size_t count)
3567032d1900STyrel Datwyler {
3568032d1900STyrel Datwyler 	struct Scsi_Host *shost = class_to_shost(dev);
3569032d1900STyrel Datwyler 	struct ibmvfc_host *vhost = shost_priv(shost);
3570032d1900STyrel Datwyler 	unsigned long flags = 0;
3571032d1900STyrel Datwyler 	unsigned int channels;
3572032d1900STyrel Datwyler 
3573032d1900STyrel Datwyler 	spin_lock_irqsave(shost->host_lock, flags);
3574032d1900STyrel Datwyler 	channels = simple_strtoul(buf, NULL, 10);
3575032d1900STyrel Datwyler 	vhost->client_scsi_channels = min(channels, nr_scsi_hw_queues);
3576032d1900STyrel Datwyler 	ibmvfc_hard_reset_host(vhost);
3577032d1900STyrel Datwyler 	spin_unlock_irqrestore(shost->host_lock, flags);
3578032d1900STyrel Datwyler 	return strlen(buf);
3579032d1900STyrel Datwyler }
3580032d1900STyrel Datwyler 
358185e2399eSBrian King static DEVICE_ATTR(partition_name, S_IRUGO, ibmvfc_show_host_partition_name, NULL);
358285e2399eSBrian King static DEVICE_ATTR(device_name, S_IRUGO, ibmvfc_show_host_device_name, NULL);
358385e2399eSBrian King static DEVICE_ATTR(port_loc_code, S_IRUGO, ibmvfc_show_host_loc_code, NULL);
358485e2399eSBrian King static DEVICE_ATTR(drc_name, S_IRUGO, ibmvfc_show_host_drc_name, NULL);
358585e2399eSBrian King static DEVICE_ATTR(npiv_version, S_IRUGO, ibmvfc_show_host_npiv_version, NULL);
3586497f9c50SBrian King static DEVICE_ATTR(capabilities, S_IRUGO, ibmvfc_show_host_capabilities, NULL);
358785e2399eSBrian King static DEVICE_ATTR(log_level, S_IRUGO | S_IWUSR,
358885e2399eSBrian King 		   ibmvfc_show_log_level, ibmvfc_store_log_level);
3589032d1900STyrel Datwyler static DEVICE_ATTR(nr_scsi_channels, S_IRUGO | S_IWUSR,
3590032d1900STyrel Datwyler 		   ibmvfc_show_scsi_channels, ibmvfc_store_scsi_channels);
3591072b91f9SBrian King 
3592072b91f9SBrian King #ifdef CONFIG_SCSI_IBMVFC_TRACE
3593072b91f9SBrian King /**
3594072b91f9SBrian King  * ibmvfc_read_trace - Dump the adapter trace
35952c3c8beaSChris Wright  * @filp:		open sysfs file
3596072b91f9SBrian King  * @kobj:		kobject struct
3597072b91f9SBrian King  * @bin_attr:	bin_attribute struct
3598072b91f9SBrian King  * @buf:		buffer
3599072b91f9SBrian King  * @off:		offset
3600072b91f9SBrian King  * @count:		buffer size
3601072b91f9SBrian King  *
3602072b91f9SBrian King  * Return value:
3603072b91f9SBrian King  *	number of bytes printed to buffer
3604072b91f9SBrian King  **/
ibmvfc_read_trace(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)36052c3c8beaSChris Wright static ssize_t ibmvfc_read_trace(struct file *filp, struct kobject *kobj,
3606072b91f9SBrian King 				 struct bin_attribute *bin_attr,
3607072b91f9SBrian King 				 char *buf, loff_t off, size_t count)
3608072b91f9SBrian King {
360918c2a59aSJiapeng Chong 	struct device *dev = kobj_to_dev(kobj);
3610072b91f9SBrian King 	struct Scsi_Host *shost = class_to_shost(dev);
3611072b91f9SBrian King 	struct ibmvfc_host *vhost = shost_priv(shost);
3612072b91f9SBrian King 	unsigned long flags = 0;
3613072b91f9SBrian King 	int size = IBMVFC_TRACE_SIZE;
3614072b91f9SBrian King 	char *src = (char *)vhost->trace;
3615072b91f9SBrian King 
3616072b91f9SBrian King 	if (off > size)
3617072b91f9SBrian King 		return 0;
3618072b91f9SBrian King 	if (off + count > size) {
3619072b91f9SBrian King 		size -= off;
3620072b91f9SBrian King 		count = size;
3621072b91f9SBrian King 	}
3622072b91f9SBrian King 
3623072b91f9SBrian King 	spin_lock_irqsave(shost->host_lock, flags);
3624072b91f9SBrian King 	memcpy(buf, &src[off], count);
3625072b91f9SBrian King 	spin_unlock_irqrestore(shost->host_lock, flags);
3626072b91f9SBrian King 	return count;
3627072b91f9SBrian King }
3628072b91f9SBrian King 
3629072b91f9SBrian King static struct bin_attribute ibmvfc_trace_attr = {
3630072b91f9SBrian King 	.attr =	{
3631072b91f9SBrian King 		.name = "trace",
3632072b91f9SBrian King 		.mode = S_IRUGO,
3633072b91f9SBrian King 	},
3634072b91f9SBrian King 	.size = 0,
3635072b91f9SBrian King 	.read = ibmvfc_read_trace,
3636072b91f9SBrian King };
3637072b91f9SBrian King #endif
3638072b91f9SBrian King 
36397adbf68fSBart Van Assche static struct attribute *ibmvfc_host_attrs[] = {
36407adbf68fSBart Van Assche 	&dev_attr_partition_name.attr,
36417adbf68fSBart Van Assche 	&dev_attr_device_name.attr,
36427adbf68fSBart Van Assche 	&dev_attr_port_loc_code.attr,
36437adbf68fSBart Van Assche 	&dev_attr_drc_name.attr,
36447adbf68fSBart Van Assche 	&dev_attr_npiv_version.attr,
36457adbf68fSBart Van Assche 	&dev_attr_capabilities.attr,
36467adbf68fSBart Van Assche 	&dev_attr_log_level.attr,
36477adbf68fSBart Van Assche 	&dev_attr_nr_scsi_channels.attr,
3648072b91f9SBrian King 	NULL
3649072b91f9SBrian King };
3650072b91f9SBrian King 
36517adbf68fSBart Van Assche ATTRIBUTE_GROUPS(ibmvfc_host);
36527adbf68fSBart Van Assche 
36537bced3fcSBart Van Assche static const struct scsi_host_template driver_template = {
3654072b91f9SBrian King 	.module = THIS_MODULE,
3655072b91f9SBrian King 	.name = "IBM POWER Virtual FC Adapter",
3656072b91f9SBrian King 	.proc_name = IBMVFC_NAME,
3657072b91f9SBrian King 	.queuecommand = ibmvfc_queuecommand,
3658b6a05c82SChristoph Hellwig 	.eh_timed_out = fc_eh_timed_out,
3659072b91f9SBrian King 	.eh_abort_handler = ibmvfc_eh_abort_handler,
3660072b91f9SBrian King 	.eh_device_reset_handler = ibmvfc_eh_device_reset_handler,
3661072b91f9SBrian King 	.eh_target_reset_handler = ibmvfc_eh_target_reset_handler,
3662072b91f9SBrian King 	.eh_host_reset_handler = ibmvfc_eh_host_reset_handler,
3663072b91f9SBrian King 	.slave_alloc = ibmvfc_slave_alloc,
3664072b91f9SBrian King 	.slave_configure = ibmvfc_slave_configure,
3665ad8dcffaSBrian King 	.target_alloc = ibmvfc_target_alloc,
3666072b91f9SBrian King 	.scan_finished = ibmvfc_scan_finished,
3667072b91f9SBrian King 	.change_queue_depth = ibmvfc_change_queue_depth,
3668072b91f9SBrian King 	.cmd_per_lun = 16,
3669072b91f9SBrian King 	.can_queue = IBMVFC_MAX_REQUESTS_DEFAULT,
3670072b91f9SBrian King 	.this_id = -1,
3671072b91f9SBrian King 	.sg_tablesize = SG_ALL,
3672072b91f9SBrian King 	.max_sectors = IBMVFC_MAX_SECTORS,
36737adbf68fSBart Van Assche 	.shost_groups = ibmvfc_host_groups,
3674c40ecc12SChristoph Hellwig 	.track_queue_depth = 1,
36756ae208e5STyrel Datwyler 	.host_tagset = 1,
3676072b91f9SBrian King };
3677072b91f9SBrian King 
3678072b91f9SBrian King /**
3679072b91f9SBrian King  * ibmvfc_next_async_crq - Returns the next entry in async queue
3680072b91f9SBrian King  * @vhost:	ibmvfc host struct
3681072b91f9SBrian King  *
3682072b91f9SBrian King  * Returns:
3683072b91f9SBrian King  *	Pointer to next entry in queue / NULL if empty
3684072b91f9SBrian King  **/
ibmvfc_next_async_crq(struct ibmvfc_host * vhost)3685072b91f9SBrian King static struct ibmvfc_async_crq *ibmvfc_next_async_crq(struct ibmvfc_host *vhost)
3686072b91f9SBrian King {
3687f8968665STyrel Datwyler 	struct ibmvfc_queue *async_crq = &vhost->async_crq;
3688072b91f9SBrian King 	struct ibmvfc_async_crq *crq;
3689072b91f9SBrian King 
3690f8968665STyrel Datwyler 	crq = &async_crq->msgs.async[async_crq->cur];
3691072b91f9SBrian King 	if (crq->valid & 0x80) {
3692072b91f9SBrian King 		if (++async_crq->cur == async_crq->size)
3693072b91f9SBrian King 			async_crq->cur = 0;
3694f5832fa2SBrian King 		rmb();
3695072b91f9SBrian King 	} else
3696072b91f9SBrian King 		crq = NULL;
3697072b91f9SBrian King 
3698072b91f9SBrian King 	return crq;
3699072b91f9SBrian King }
3700072b91f9SBrian King 
3701072b91f9SBrian King /**
3702072b91f9SBrian King  * ibmvfc_next_crq - Returns the next entry in message queue
3703072b91f9SBrian King  * @vhost:	ibmvfc host struct
3704072b91f9SBrian King  *
3705072b91f9SBrian King  * Returns:
3706072b91f9SBrian King  *	Pointer to next entry in queue / NULL if empty
3707072b91f9SBrian King  **/
ibmvfc_next_crq(struct ibmvfc_host * vhost)3708072b91f9SBrian King static struct ibmvfc_crq *ibmvfc_next_crq(struct ibmvfc_host *vhost)
3709072b91f9SBrian King {
3710f8968665STyrel Datwyler 	struct ibmvfc_queue *queue = &vhost->crq;
3711072b91f9SBrian King 	struct ibmvfc_crq *crq;
3712072b91f9SBrian King 
3713f8968665STyrel Datwyler 	crq = &queue->msgs.crq[queue->cur];
3714072b91f9SBrian King 	if (crq->valid & 0x80) {
3715072b91f9SBrian King 		if (++queue->cur == queue->size)
3716072b91f9SBrian King 			queue->cur = 0;
3717f5832fa2SBrian King 		rmb();
3718072b91f9SBrian King 	} else
3719072b91f9SBrian King 		crq = NULL;
3720072b91f9SBrian King 
3721072b91f9SBrian King 	return crq;
3722072b91f9SBrian King }
3723072b91f9SBrian King 
3724072b91f9SBrian King /**
3725072b91f9SBrian King  * ibmvfc_interrupt - Interrupt handler
3726072b91f9SBrian King  * @irq:		number of irq to handle, not used
3727072b91f9SBrian King  * @dev_instance: ibmvfc_host that received interrupt
3728072b91f9SBrian King  *
3729072b91f9SBrian King  * Returns:
3730072b91f9SBrian King  *	IRQ_HANDLED
3731072b91f9SBrian King  **/
ibmvfc_interrupt(int irq,void * dev_instance)3732072b91f9SBrian King static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance)
3733072b91f9SBrian King {
3734072b91f9SBrian King 	struct ibmvfc_host *vhost = (struct ibmvfc_host *)dev_instance;
3735039a0898SBrian King 	unsigned long flags;
3736039a0898SBrian King 
3737039a0898SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
3738039a0898SBrian King 	vio_disable_interrupts(to_vio_dev(vhost->dev));
3739039a0898SBrian King 	tasklet_schedule(&vhost->tasklet);
3740039a0898SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
3741039a0898SBrian King 	return IRQ_HANDLED;
3742039a0898SBrian King }
3743039a0898SBrian King 
3744039a0898SBrian King /**
3745039a0898SBrian King  * ibmvfc_tasklet - Interrupt handler tasklet
3746039a0898SBrian King  * @data:		ibmvfc host struct
3747039a0898SBrian King  *
3748039a0898SBrian King  * Returns:
3749039a0898SBrian King  *	Nothing
3750039a0898SBrian King  **/
ibmvfc_tasklet(void * data)3751039a0898SBrian King static void ibmvfc_tasklet(void *data)
3752039a0898SBrian King {
3753039a0898SBrian King 	struct ibmvfc_host *vhost = data;
3754072b91f9SBrian King 	struct vio_dev *vdev = to_vio_dev(vhost->dev);
3755072b91f9SBrian King 	struct ibmvfc_crq *crq;
3756072b91f9SBrian King 	struct ibmvfc_async_crq *async;
37571f4a4a19STyrel Datwyler 	struct ibmvfc_event *evt, *temp;
3758072b91f9SBrian King 	unsigned long flags;
3759072b91f9SBrian King 	int done = 0;
37601f4a4a19STyrel Datwyler 	LIST_HEAD(evt_doneq);
3761072b91f9SBrian King 
3762072b91f9SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
376357e80e0bSTyrel Datwyler 	spin_lock(vhost->crq.q_lock);
3764072b91f9SBrian King 	while (!done) {
3765072b91f9SBrian King 		/* Pull all the valid messages off the async CRQ */
3766072b91f9SBrian King 		while ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
3767072b91f9SBrian King 			ibmvfc_handle_async(async, vhost);
3768072b91f9SBrian King 			async->valid = 0;
3769f5832fa2SBrian King 			wmb();
3770072b91f9SBrian King 		}
3771072b91f9SBrian King 
3772f1d7fb7aSBrian King 		/* Pull all the valid messages off the CRQ */
3773f1d7fb7aSBrian King 		while ((crq = ibmvfc_next_crq(vhost)) != NULL) {
37741f4a4a19STyrel Datwyler 			ibmvfc_handle_crq(crq, vhost, &evt_doneq);
3775072b91f9SBrian King 			crq->valid = 0;
3776f5832fa2SBrian King 			wmb();
3777f1d7fb7aSBrian King 		}
3778f1d7fb7aSBrian King 
3779f1d7fb7aSBrian King 		vio_enable_interrupts(vdev);
3780f1d7fb7aSBrian King 		if ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
3781072b91f9SBrian King 			vio_disable_interrupts(vdev);
3782072b91f9SBrian King 			ibmvfc_handle_async(async, vhost);
37834081b77cSBrian King 			async->valid = 0;
3784f5832fa2SBrian King 			wmb();
3785f1d7fb7aSBrian King 		} else if ((crq = ibmvfc_next_crq(vhost)) != NULL) {
3786f1d7fb7aSBrian King 			vio_disable_interrupts(vdev);
37871f4a4a19STyrel Datwyler 			ibmvfc_handle_crq(crq, vhost, &evt_doneq);
3788f1d7fb7aSBrian King 			crq->valid = 0;
3789f5832fa2SBrian King 			wmb();
3790072b91f9SBrian King 		} else
3791072b91f9SBrian King 			done = 1;
3792072b91f9SBrian King 	}
3793072b91f9SBrian King 
379457e80e0bSTyrel Datwyler 	spin_unlock(vhost->crq.q_lock);
3795072b91f9SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
37961f4a4a19STyrel Datwyler 
37971f4a4a19STyrel Datwyler 	list_for_each_entry_safe(evt, temp, &evt_doneq, queue_list) {
37981f4a4a19STyrel Datwyler 		del_timer(&evt->timer);
37991f4a4a19STyrel Datwyler 		list_del(&evt->queue_list);
38001f4a4a19STyrel Datwyler 		ibmvfc_trc_end(evt);
38011f4a4a19STyrel Datwyler 		evt->done(evt);
38021f4a4a19STyrel Datwyler 	}
3803072b91f9SBrian King }
3804072b91f9SBrian King 
ibmvfc_toggle_scrq_irq(struct ibmvfc_queue * scrq,int enable)3805d20046e6STyrel Datwyler static int ibmvfc_toggle_scrq_irq(struct ibmvfc_queue *scrq, int enable)
3806d20046e6STyrel Datwyler {
3807d20046e6STyrel Datwyler 	struct device *dev = scrq->vhost->dev;
3808d20046e6STyrel Datwyler 	struct vio_dev *vdev = to_vio_dev(dev);
3809d20046e6STyrel Datwyler 	unsigned long rc;
3810d20046e6STyrel Datwyler 	int irq_action = H_ENABLE_VIO_INTERRUPT;
3811d20046e6STyrel Datwyler 
3812d20046e6STyrel Datwyler 	if (!enable)
3813d20046e6STyrel Datwyler 		irq_action = H_DISABLE_VIO_INTERRUPT;
3814d20046e6STyrel Datwyler 
3815d20046e6STyrel Datwyler 	rc = plpar_hcall_norets(H_VIOCTL, vdev->unit_address, irq_action,
3816d20046e6STyrel Datwyler 				scrq->hw_irq, 0, 0);
3817d20046e6STyrel Datwyler 
3818d20046e6STyrel Datwyler 	if (rc)
3819d20046e6STyrel Datwyler 		dev_err(dev, "Couldn't %s sub-crq[%lu] irq. rc=%ld\n",
3820d20046e6STyrel Datwyler 			enable ? "enable" : "disable", scrq->hwq_id, rc);
3821d20046e6STyrel Datwyler 
3822d20046e6STyrel Datwyler 	return rc;
3823d20046e6STyrel Datwyler }
3824d20046e6STyrel Datwyler 
ibmvfc_handle_scrq(struct ibmvfc_crq * crq,struct ibmvfc_host * vhost,struct list_head * evt_doneq)38251d956ad8STyrel Datwyler static void ibmvfc_handle_scrq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost,
38261d956ad8STyrel Datwyler 			       struct list_head *evt_doneq)
38271d956ad8STyrel Datwyler {
38281d956ad8STyrel Datwyler 	struct ibmvfc_event *evt = (struct ibmvfc_event *)be64_to_cpu(crq->ioba);
38291d956ad8STyrel Datwyler 
38301d956ad8STyrel Datwyler 	switch (crq->valid) {
38311d956ad8STyrel Datwyler 	case IBMVFC_CRQ_CMD_RSP:
38321d956ad8STyrel Datwyler 		break;
38331d956ad8STyrel Datwyler 	case IBMVFC_CRQ_XPORT_EVENT:
38341d956ad8STyrel Datwyler 		return;
38351d956ad8STyrel Datwyler 	default:
38361d956ad8STyrel Datwyler 		dev_err(vhost->dev, "Got and invalid message type 0x%02x\n", crq->valid);
38371d956ad8STyrel Datwyler 		return;
38381d956ad8STyrel Datwyler 	}
38391d956ad8STyrel Datwyler 
38401d956ad8STyrel Datwyler 	/* The only kind of payload CRQs we should get are responses to
38411d956ad8STyrel Datwyler 	 * things we send. Make sure this response is to something we
38421d956ad8STyrel Datwyler 	 * actually sent
38431d956ad8STyrel Datwyler 	 */
38441d956ad8STyrel Datwyler 	if (unlikely(!ibmvfc_valid_event(&evt->queue->evt_pool, evt))) {
38451d956ad8STyrel Datwyler 		dev_err(vhost->dev, "Returned correlation_token 0x%08llx is invalid!\n",
38461d956ad8STyrel Datwyler 			crq->ioba);
38471d956ad8STyrel Datwyler 		return;
38481d956ad8STyrel Datwyler 	}
38491d956ad8STyrel Datwyler 
3850a264cf5eSTyrel Datwyler 	if (unlikely(atomic_dec_if_positive(&evt->active))) {
38511d956ad8STyrel Datwyler 		dev_err(vhost->dev, "Received duplicate correlation_token 0x%08llx!\n",
38521d956ad8STyrel Datwyler 			crq->ioba);
38531d956ad8STyrel Datwyler 		return;
38541d956ad8STyrel Datwyler 	}
38551d956ad8STyrel Datwyler 
38561d956ad8STyrel Datwyler 	spin_lock(&evt->queue->l_lock);
38571d956ad8STyrel Datwyler 	list_move_tail(&evt->queue_list, evt_doneq);
38581d956ad8STyrel Datwyler 	spin_unlock(&evt->queue->l_lock);
38591d956ad8STyrel Datwyler }
38601d956ad8STyrel Datwyler 
ibmvfc_next_scrq(struct ibmvfc_queue * scrq)38611d956ad8STyrel Datwyler static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq)
38621d956ad8STyrel Datwyler {
38631d956ad8STyrel Datwyler 	struct ibmvfc_crq *crq;
38641d956ad8STyrel Datwyler 
38651d956ad8STyrel Datwyler 	crq = &scrq->msgs.scrq[scrq->cur].crq;
38661d956ad8STyrel Datwyler 	if (crq->valid & 0x80) {
38671d956ad8STyrel Datwyler 		if (++scrq->cur == scrq->size)
38681d956ad8STyrel Datwyler 			scrq->cur = 0;
38691d956ad8STyrel Datwyler 		rmb();
38701d956ad8STyrel Datwyler 	} else
38711d956ad8STyrel Datwyler 		crq = NULL;
38721d956ad8STyrel Datwyler 
38731d956ad8STyrel Datwyler 	return crq;
38741d956ad8STyrel Datwyler }
38751d956ad8STyrel Datwyler 
ibmvfc_drain_sub_crq(struct ibmvfc_queue * scrq)38761d956ad8STyrel Datwyler static void ibmvfc_drain_sub_crq(struct ibmvfc_queue *scrq)
38771d956ad8STyrel Datwyler {
38781d956ad8STyrel Datwyler 	struct ibmvfc_crq *crq;
38791d956ad8STyrel Datwyler 	struct ibmvfc_event *evt, *temp;
38801d956ad8STyrel Datwyler 	unsigned long flags;
38811d956ad8STyrel Datwyler 	int done = 0;
38821d956ad8STyrel Datwyler 	LIST_HEAD(evt_doneq);
38831d956ad8STyrel Datwyler 
38841d956ad8STyrel Datwyler 	spin_lock_irqsave(scrq->q_lock, flags);
38851d956ad8STyrel Datwyler 	while (!done) {
38861d956ad8STyrel Datwyler 		while ((crq = ibmvfc_next_scrq(scrq)) != NULL) {
38871d956ad8STyrel Datwyler 			ibmvfc_handle_scrq(crq, scrq->vhost, &evt_doneq);
38881d956ad8STyrel Datwyler 			crq->valid = 0;
38891d956ad8STyrel Datwyler 			wmb();
38901d956ad8STyrel Datwyler 		}
38911d956ad8STyrel Datwyler 
38921d956ad8STyrel Datwyler 		ibmvfc_toggle_scrq_irq(scrq, 1);
38931d956ad8STyrel Datwyler 		if ((crq = ibmvfc_next_scrq(scrq)) != NULL) {
38941d956ad8STyrel Datwyler 			ibmvfc_toggle_scrq_irq(scrq, 0);
38951d956ad8STyrel Datwyler 			ibmvfc_handle_scrq(crq, scrq->vhost, &evt_doneq);
38961d956ad8STyrel Datwyler 			crq->valid = 0;
38971d956ad8STyrel Datwyler 			wmb();
38981d956ad8STyrel Datwyler 		} else
38991d956ad8STyrel Datwyler 			done = 1;
39001d956ad8STyrel Datwyler 	}
39011d956ad8STyrel Datwyler 	spin_unlock_irqrestore(scrq->q_lock, flags);
39021d956ad8STyrel Datwyler 
39031d956ad8STyrel Datwyler 	list_for_each_entry_safe(evt, temp, &evt_doneq, queue_list) {
39041d956ad8STyrel Datwyler 		del_timer(&evt->timer);
39051d956ad8STyrel Datwyler 		list_del(&evt->queue_list);
39061d956ad8STyrel Datwyler 		ibmvfc_trc_end(evt);
39071d956ad8STyrel Datwyler 		evt->done(evt);
39081d956ad8STyrel Datwyler 	}
39091d956ad8STyrel Datwyler }
39101d956ad8STyrel Datwyler 
ibmvfc_interrupt_scsi(int irq,void * scrq_instance)391180a9e8eaSTyrel Datwyler static irqreturn_t ibmvfc_interrupt_scsi(int irq, void *scrq_instance)
391280a9e8eaSTyrel Datwyler {
391380a9e8eaSTyrel Datwyler 	struct ibmvfc_queue *scrq = (struct ibmvfc_queue *)scrq_instance;
391480a9e8eaSTyrel Datwyler 
391580a9e8eaSTyrel Datwyler 	ibmvfc_toggle_scrq_irq(scrq, 0);
391680a9e8eaSTyrel Datwyler 	ibmvfc_drain_sub_crq(scrq);
391780a9e8eaSTyrel Datwyler 
391880a9e8eaSTyrel Datwyler 	return IRQ_HANDLED;
391980a9e8eaSTyrel Datwyler }
392080a9e8eaSTyrel Datwyler 
3921072b91f9SBrian King /**
3922072b91f9SBrian King  * ibmvfc_init_tgt - Set the next init job step for the target
3923072b91f9SBrian King  * @tgt:		ibmvfc target struct
3924072b91f9SBrian King  * @job_step:	job step to perform
3925072b91f9SBrian King  *
3926072b91f9SBrian King  **/
ibmvfc_init_tgt(struct ibmvfc_target * tgt,void (* job_step)(struct ibmvfc_target *))3927072b91f9SBrian King static void ibmvfc_init_tgt(struct ibmvfc_target *tgt,
3928072b91f9SBrian King 			    void (*job_step) (struct ibmvfc_target *))
3929072b91f9SBrian King {
3930ed830385SBrian King 	if (!ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT))
3931072b91f9SBrian King 		tgt->job_step = job_step;
3932072b91f9SBrian King 	wake_up(&tgt->vhost->work_wait_q);
3933072b91f9SBrian King }
3934072b91f9SBrian King 
3935072b91f9SBrian King /**
3936072b91f9SBrian King  * ibmvfc_retry_tgt_init - Attempt to retry a step in target initialization
3937072b91f9SBrian King  * @tgt:		ibmvfc target struct
3938072b91f9SBrian King  * @job_step:	initialization job step
3939072b91f9SBrian King  *
39407d0e4622SBrian King  * Returns: 1 if step will be retried / 0 if not
39417d0e4622SBrian King  *
3942072b91f9SBrian King  **/
ibmvfc_retry_tgt_init(struct ibmvfc_target * tgt,void (* job_step)(struct ibmvfc_target *))39437d0e4622SBrian King static int ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
3944072b91f9SBrian King 				  void (*job_step) (struct ibmvfc_target *))
3945072b91f9SBrian King {
39461c41fa82SBrian King 	if (++tgt->init_retries > IBMVFC_MAX_TGT_INIT_RETRIES) {
3947ed830385SBrian King 		ibmvfc_del_tgt(tgt);
3948072b91f9SBrian King 		wake_up(&tgt->vhost->work_wait_q);
39497d0e4622SBrian King 		return 0;
3950072b91f9SBrian King 	} else
3951072b91f9SBrian King 		ibmvfc_init_tgt(tgt, job_step);
39527d0e4622SBrian King 	return 1;
3953072b91f9SBrian King }
3954072b91f9SBrian King 
3955a3b7aeabSBrian King /* Defined in FC-LS */
3956a3b7aeabSBrian King static const struct {
3957a3b7aeabSBrian King 	int code;
3958a3b7aeabSBrian King 	int retry;
3959a3b7aeabSBrian King 	int logged_in;
3960a3b7aeabSBrian King } prli_rsp [] = {
3961a3b7aeabSBrian King 	{ 0, 1, 0 },
3962a3b7aeabSBrian King 	{ 1, 0, 1 },
3963a3b7aeabSBrian King 	{ 2, 1, 0 },
3964a3b7aeabSBrian King 	{ 3, 1, 0 },
3965a3b7aeabSBrian King 	{ 4, 0, 0 },
3966a3b7aeabSBrian King 	{ 5, 0, 0 },
3967a3b7aeabSBrian King 	{ 6, 0, 1 },
3968a3b7aeabSBrian King 	{ 7, 0, 0 },
3969a3b7aeabSBrian King 	{ 8, 1, 0 },
3970a3b7aeabSBrian King };
3971a3b7aeabSBrian King 
3972a3b7aeabSBrian King /**
3973a3b7aeabSBrian King  * ibmvfc_get_prli_rsp - Find PRLI response index
3974a3b7aeabSBrian King  * @flags:	PRLI response flags
3975a3b7aeabSBrian King  *
3976a3b7aeabSBrian King  **/
ibmvfc_get_prli_rsp(u16 flags)3977a3b7aeabSBrian King static int ibmvfc_get_prli_rsp(u16 flags)
3978a3b7aeabSBrian King {
3979a3b7aeabSBrian King 	int i;
3980a3b7aeabSBrian King 	int code = (flags & 0x0f00) >> 8;
3981a3b7aeabSBrian King 
3982a3b7aeabSBrian King 	for (i = 0; i < ARRAY_SIZE(prli_rsp); i++)
3983a3b7aeabSBrian King 		if (prli_rsp[i].code == code)
3984a3b7aeabSBrian King 			return i;
3985a3b7aeabSBrian King 
3986a3b7aeabSBrian King 	return 0;
3987a3b7aeabSBrian King }
3988a3b7aeabSBrian King 
3989072b91f9SBrian King /**
3990072b91f9SBrian King  * ibmvfc_tgt_prli_done - Completion handler for Process Login
3991072b91f9SBrian King  * @evt:	ibmvfc event struct
3992072b91f9SBrian King  *
3993072b91f9SBrian King  **/
ibmvfc_tgt_prli_done(struct ibmvfc_event * evt)3994072b91f9SBrian King static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
3995072b91f9SBrian King {
3996072b91f9SBrian King 	struct ibmvfc_target *tgt = evt->tgt;
3997072b91f9SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
3998072b91f9SBrian King 	struct ibmvfc_process_login *rsp = &evt->xfer_iu->prli;
3999a3b7aeabSBrian King 	struct ibmvfc_prli_svc_parms *parms = &rsp->parms;
40000aab6c3fSTyrel Datwyler 	u32 status = be16_to_cpu(rsp->common.status);
40017d0e4622SBrian King 	int index, level = IBMVFC_DEFAULT_LOG_LEVEL;
4002072b91f9SBrian King 
4003072b91f9SBrian King 	vhost->discovery_threads--;
4004072b91f9SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4005072b91f9SBrian King 	switch (status) {
4006072b91f9SBrian King 	case IBMVFC_MAD_SUCCESS:
4007a3b7aeabSBrian King 		tgt_dbg(tgt, "Process Login succeeded: %X %02X %04X\n",
4008a3b7aeabSBrian King 			parms->type, parms->flags, parms->service_parms);
4009a3b7aeabSBrian King 
4010a3b7aeabSBrian King 		if (parms->type == IBMVFC_SCSI_FCP_TYPE) {
40110aab6c3fSTyrel Datwyler 			index = ibmvfc_get_prli_rsp(be16_to_cpu(parms->flags));
4012a3b7aeabSBrian King 			if (prli_rsp[index].logged_in) {
40130aab6c3fSTyrel Datwyler 				if (be16_to_cpu(parms->flags) & IBMVFC_PRLI_EST_IMG_PAIR) {
4014072b91f9SBrian King 					tgt->need_login = 0;
4015a3b7aeabSBrian King 					tgt->ids.roles = 0;
40160aab6c3fSTyrel Datwyler 					if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_TARGET_FUNC)
4017a3b7aeabSBrian King 						tgt->ids.roles |= FC_PORT_ROLE_FCP_TARGET;
40180aab6c3fSTyrel Datwyler 					if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_INITIATOR_FUNC)
4019a3b7aeabSBrian King 						tgt->ids.roles |= FC_PORT_ROLE_FCP_INITIATOR;
402043c8da90SBrian King 					tgt->add_rport = 1;
4021a3b7aeabSBrian King 				} else
4022ed830385SBrian King 					ibmvfc_del_tgt(tgt);
4023a3b7aeabSBrian King 			} else if (prli_rsp[index].retry)
4024a3b7aeabSBrian King 				ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
4025a3b7aeabSBrian King 			else
4026ed830385SBrian King 				ibmvfc_del_tgt(tgt);
4027a3b7aeabSBrian King 		} else
4028ed830385SBrian King 			ibmvfc_del_tgt(tgt);
4029072b91f9SBrian King 		break;
4030072b91f9SBrian King 	case IBMVFC_MAD_DRIVER_FAILED:
4031072b91f9SBrian King 		break;
4032072b91f9SBrian King 	case IBMVFC_MAD_CRQ_ERROR:
4033072b91f9SBrian King 		ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
4034072b91f9SBrian King 		break;
4035072b91f9SBrian King 	case IBMVFC_MAD_FAILED:
4036072b91f9SBrian King 	default:
40370aab6c3fSTyrel Datwyler 		if ((be16_to_cpu(rsp->status) & IBMVFC_VIOS_FAILURE) &&
40380aab6c3fSTyrel Datwyler 		     be16_to_cpu(rsp->error) == IBMVFC_PLOGI_REQUIRED)
4039017b2ae3SBrian King 			level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
4040017b2ae3SBrian King 		else if (tgt->logo_rcvd)
4041017b2ae3SBrian King 			level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
40420aab6c3fSTyrel Datwyler 		else if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
40437d0e4622SBrian King 			level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
404410e79499SBrian King 		else
4045ed830385SBrian King 			ibmvfc_del_tgt(tgt);
40467d0e4622SBrian King 
40477d0e4622SBrian King 		tgt_log(tgt, level, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
40480aab6c3fSTyrel Datwyler 			ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
40493e6f7de4STyrel Datwyler 			be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), status);
4050072b91f9SBrian King 		break;
4051f36cfe6aSChristopher Díaz Riveros 	}
4052072b91f9SBrian King 
4053072b91f9SBrian King 	kref_put(&tgt->kref, ibmvfc_release_tgt);
4054072b91f9SBrian King 	ibmvfc_free_event(evt);
4055072b91f9SBrian King 	wake_up(&vhost->work_wait_q);
4056072b91f9SBrian King }
4057072b91f9SBrian King 
4058072b91f9SBrian King /**
4059072b91f9SBrian King  * ibmvfc_tgt_send_prli - Send a process login
4060072b91f9SBrian King  * @tgt:	ibmvfc target struct
4061072b91f9SBrian King  *
4062072b91f9SBrian King  **/
ibmvfc_tgt_send_prli(struct ibmvfc_target * tgt)4063072b91f9SBrian King static void ibmvfc_tgt_send_prli(struct ibmvfc_target *tgt)
4064072b91f9SBrian King {
4065072b91f9SBrian King 	struct ibmvfc_process_login *prli;
4066072b91f9SBrian King 	struct ibmvfc_host *vhost = tgt->vhost;
4067072b91f9SBrian King 	struct ibmvfc_event *evt;
4068072b91f9SBrian King 
4069072b91f9SBrian King 	if (vhost->discovery_threads >= disc_threads)
4070072b91f9SBrian King 		return;
4071072b91f9SBrian King 
4072072b91f9SBrian King 	kref_get(&tgt->kref);
4073e4b26f3dSTyrel Datwyler 	evt = ibmvfc_get_event(&vhost->crq);
4074*8bbe784cSTyrel Datwyler 	if (!evt) {
4075*8bbe784cSTyrel Datwyler 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4076*8bbe784cSTyrel Datwyler 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4077*8bbe784cSTyrel Datwyler 		__ibmvfc_reset_host(vhost);
4078*8bbe784cSTyrel Datwyler 		return;
4079*8bbe784cSTyrel Datwyler 	}
4080072b91f9SBrian King 	vhost->discovery_threads++;
4081072b91f9SBrian King 	ibmvfc_init_event(evt, ibmvfc_tgt_prli_done, IBMVFC_MAD_FORMAT);
4082072b91f9SBrian King 	evt->tgt = tgt;
4083072b91f9SBrian King 	prli = &evt->iu.prli;
4084072b91f9SBrian King 	memset(prli, 0, sizeof(*prli));
4085ebc7c74bSTyrel Datwyler 	if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
4086ebc7c74bSTyrel Datwyler 		prli->common.version = cpu_to_be32(2);
4087ebc7c74bSTyrel Datwyler 		prli->target_wwpn = cpu_to_be64(tgt->wwpn);
4088ebc7c74bSTyrel Datwyler 	} else {
40890aab6c3fSTyrel Datwyler 		prli->common.version = cpu_to_be32(1);
4090ebc7c74bSTyrel Datwyler 	}
40910aab6c3fSTyrel Datwyler 	prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN);
40920aab6c3fSTyrel Datwyler 	prli->common.length = cpu_to_be16(sizeof(*prli));
40930aab6c3fSTyrel Datwyler 	prli->scsi_id = cpu_to_be64(tgt->scsi_id);
4094072b91f9SBrian King 
4095072b91f9SBrian King 	prli->parms.type = IBMVFC_SCSI_FCP_TYPE;
40960aab6c3fSTyrel Datwyler 	prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR);
40970aab6c3fSTyrel Datwyler 	prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC);
4098bb5a5054STyrel Datwyler 	prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED);
4099072b91f9SBrian King 
4100a6104b1eSTyrel Datwyler 	if (cls3_error)
4101a6104b1eSTyrel Datwyler 		prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY);
4102a6104b1eSTyrel Datwyler 
4103072b91f9SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
4104072b91f9SBrian King 	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
4105072b91f9SBrian King 		vhost->discovery_threads--;
4106072b91f9SBrian King 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4107072b91f9SBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4108072b91f9SBrian King 	} else
4109072b91f9SBrian King 		tgt_dbg(tgt, "Sent process login\n");
4110072b91f9SBrian King }
4111072b91f9SBrian King 
4112072b91f9SBrian King /**
4113072b91f9SBrian King  * ibmvfc_tgt_plogi_done - Completion handler for Port Login
4114072b91f9SBrian King  * @evt:	ibmvfc event struct
4115072b91f9SBrian King  *
4116072b91f9SBrian King  **/
ibmvfc_tgt_plogi_done(struct ibmvfc_event * evt)4117072b91f9SBrian King static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
4118072b91f9SBrian King {
4119072b91f9SBrian King 	struct ibmvfc_target *tgt = evt->tgt;
4120072b91f9SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
4121072b91f9SBrian King 	struct ibmvfc_port_login *rsp = &evt->xfer_iu->plogi;
41220aab6c3fSTyrel Datwyler 	u32 status = be16_to_cpu(rsp->common.status);
41237d0e4622SBrian King 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
4124072b91f9SBrian King 
4125072b91f9SBrian King 	vhost->discovery_threads--;
4126072b91f9SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4127072b91f9SBrian King 	switch (status) {
4128072b91f9SBrian King 	case IBMVFC_MAD_SUCCESS:
4129072b91f9SBrian King 		tgt_dbg(tgt, "Port Login succeeded\n");
4130072b91f9SBrian King 		if (tgt->ids.port_name &&
4131072b91f9SBrian King 		    tgt->ids.port_name != wwn_to_u64(rsp->service_parms.port_name)) {
4132072b91f9SBrian King 			vhost->reinit = 1;
4133072b91f9SBrian King 			tgt_dbg(tgt, "Port re-init required\n");
4134072b91f9SBrian King 			break;
4135072b91f9SBrian King 		}
4136072b91f9SBrian King 		tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name);
4137072b91f9SBrian King 		tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name);
4138072b91f9SBrian King 		tgt->ids.port_id = tgt->scsi_id;
4139072b91f9SBrian King 		memcpy(&tgt->service_parms, &rsp->service_parms,
4140072b91f9SBrian King 		       sizeof(tgt->service_parms));
4141072b91f9SBrian King 		memcpy(&tgt->service_parms_change, &rsp->service_parms_change,
4142072b91f9SBrian King 		       sizeof(tgt->service_parms_change));
4143072b91f9SBrian King 		ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_prli);
4144072b91f9SBrian King 		break;
4145072b91f9SBrian King 	case IBMVFC_MAD_DRIVER_FAILED:
4146072b91f9SBrian King 		break;
4147072b91f9SBrian King 	case IBMVFC_MAD_CRQ_ERROR:
4148072b91f9SBrian King 		ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
4149072b91f9SBrian King 		break;
4150072b91f9SBrian King 	case IBMVFC_MAD_FAILED:
4151072b91f9SBrian King 	default:
41520aab6c3fSTyrel Datwyler 		if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
41537d0e4622SBrian King 			level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
41547d0e4622SBrian King 		else
4155ed830385SBrian King 			ibmvfc_del_tgt(tgt);
41567d0e4622SBrian King 
41577d0e4622SBrian King 		tgt_log(tgt, level, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
41583e6f7de4STyrel Datwyler 			ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
41593e6f7de4STyrel Datwyler 					     be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
41603e6f7de4STyrel Datwyler 			ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type),
41613e6f7de4STyrel Datwyler 			ibmvfc_get_ls_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain), status);
4162072b91f9SBrian King 		break;
4163f36cfe6aSChristopher Díaz Riveros 	}
4164072b91f9SBrian King 
4165072b91f9SBrian King 	kref_put(&tgt->kref, ibmvfc_release_tgt);
4166072b91f9SBrian King 	ibmvfc_free_event(evt);
4167072b91f9SBrian King 	wake_up(&vhost->work_wait_q);
4168072b91f9SBrian King }
4169072b91f9SBrian King 
4170072b91f9SBrian King /**
4171072b91f9SBrian King  * ibmvfc_tgt_send_plogi - Send PLOGI to the specified target
4172072b91f9SBrian King  * @tgt:	ibmvfc target struct
4173072b91f9SBrian King  *
4174072b91f9SBrian King  **/
ibmvfc_tgt_send_plogi(struct ibmvfc_target * tgt)4175072b91f9SBrian King static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt)
4176072b91f9SBrian King {
4177072b91f9SBrian King 	struct ibmvfc_port_login *plogi;
4178072b91f9SBrian King 	struct ibmvfc_host *vhost = tgt->vhost;
4179072b91f9SBrian King 	struct ibmvfc_event *evt;
4180072b91f9SBrian King 
4181072b91f9SBrian King 	if (vhost->discovery_threads >= disc_threads)
4182072b91f9SBrian King 		return;
4183072b91f9SBrian King 
4184072b91f9SBrian King 	kref_get(&tgt->kref);
4185017b2ae3SBrian King 	tgt->logo_rcvd = 0;
4186e4b26f3dSTyrel Datwyler 	evt = ibmvfc_get_event(&vhost->crq);
4187*8bbe784cSTyrel Datwyler 	if (!evt) {
4188*8bbe784cSTyrel Datwyler 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4189*8bbe784cSTyrel Datwyler 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4190*8bbe784cSTyrel Datwyler 		__ibmvfc_reset_host(vhost);
4191*8bbe784cSTyrel Datwyler 		return;
4192*8bbe784cSTyrel Datwyler 	}
4193072b91f9SBrian King 	vhost->discovery_threads++;
4194072b91f9SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
4195072b91f9SBrian King 	ibmvfc_init_event(evt, ibmvfc_tgt_plogi_done, IBMVFC_MAD_FORMAT);
4196072b91f9SBrian King 	evt->tgt = tgt;
4197072b91f9SBrian King 	plogi = &evt->iu.plogi;
4198072b91f9SBrian King 	memset(plogi, 0, sizeof(*plogi));
4199ebc7c74bSTyrel Datwyler 	if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
4200ebc7c74bSTyrel Datwyler 		plogi->common.version = cpu_to_be32(2);
4201ebc7c74bSTyrel Datwyler 		plogi->target_wwpn = cpu_to_be64(tgt->wwpn);
4202ebc7c74bSTyrel Datwyler 	} else {
42030aab6c3fSTyrel Datwyler 		plogi->common.version = cpu_to_be32(1);
4204ebc7c74bSTyrel Datwyler 	}
42050aab6c3fSTyrel Datwyler 	plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN);
42060aab6c3fSTyrel Datwyler 	plogi->common.length = cpu_to_be16(sizeof(*plogi));
42070aab6c3fSTyrel Datwyler 	plogi->scsi_id = cpu_to_be64(tgt->scsi_id);
4208072b91f9SBrian King 
4209072b91f9SBrian King 	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
4210072b91f9SBrian King 		vhost->discovery_threads--;
4211072b91f9SBrian King 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4212072b91f9SBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4213072b91f9SBrian King 	} else
4214072b91f9SBrian King 		tgt_dbg(tgt, "Sent port login\n");
4215072b91f9SBrian King }
4216072b91f9SBrian King 
4217072b91f9SBrian King /**
4218072b91f9SBrian King  * ibmvfc_tgt_implicit_logout_done - Completion handler for Implicit Logout MAD
4219072b91f9SBrian King  * @evt:	ibmvfc event struct
4220072b91f9SBrian King  *
4221072b91f9SBrian King  **/
ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event * evt)4222072b91f9SBrian King static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt)
4223072b91f9SBrian King {
4224072b91f9SBrian King 	struct ibmvfc_target *tgt = evt->tgt;
4225072b91f9SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
4226072b91f9SBrian King 	struct ibmvfc_implicit_logout *rsp = &evt->xfer_iu->implicit_logout;
42270aab6c3fSTyrel Datwyler 	u32 status = be16_to_cpu(rsp->common.status);
4228072b91f9SBrian King 
4229072b91f9SBrian King 	vhost->discovery_threads--;
4230072b91f9SBrian King 	ibmvfc_free_event(evt);
4231072b91f9SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4232072b91f9SBrian King 
4233072b91f9SBrian King 	switch (status) {
4234072b91f9SBrian King 	case IBMVFC_MAD_SUCCESS:
4235072b91f9SBrian King 		tgt_dbg(tgt, "Implicit Logout succeeded\n");
4236072b91f9SBrian King 		break;
4237072b91f9SBrian King 	case IBMVFC_MAD_DRIVER_FAILED:
4238072b91f9SBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4239072b91f9SBrian King 		wake_up(&vhost->work_wait_q);
4240072b91f9SBrian King 		return;
4241072b91f9SBrian King 	case IBMVFC_MAD_FAILED:
4242072b91f9SBrian King 	default:
4243072b91f9SBrian King 		tgt_err(tgt, "Implicit Logout failed: rc=0x%02X\n", status);
4244072b91f9SBrian King 		break;
4245f36cfe6aSChristopher Díaz Riveros 	}
4246072b91f9SBrian King 
4247072b91f9SBrian King 	ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_plogi);
4248072b91f9SBrian King 	kref_put(&tgt->kref, ibmvfc_release_tgt);
4249072b91f9SBrian King 	wake_up(&vhost->work_wait_q);
4250072b91f9SBrian King }
4251072b91f9SBrian King 
4252072b91f9SBrian King /**
4253ed830385SBrian King  * __ibmvfc_tgt_get_implicit_logout_evt - Allocate and init an event for implicit logout
4254ed830385SBrian King  * @tgt:		ibmvfc target struct
4255dd9c7729SLee Jones  * @done:		Routine to call when the event is responded to
4256ed830385SBrian King  *
4257ed830385SBrian King  * Returns:
4258ed830385SBrian King  *	Allocated and initialized ibmvfc_event struct
4259ed830385SBrian King  **/
__ibmvfc_tgt_get_implicit_logout_evt(struct ibmvfc_target * tgt,void (* done)(struct ibmvfc_event *))4260ed830385SBrian King static struct ibmvfc_event *__ibmvfc_tgt_get_implicit_logout_evt(struct ibmvfc_target *tgt,
4261ed830385SBrian King 								 void (*done) (struct ibmvfc_event *))
4262ed830385SBrian King {
4263ed830385SBrian King 	struct ibmvfc_implicit_logout *mad;
4264ed830385SBrian King 	struct ibmvfc_host *vhost = tgt->vhost;
4265ed830385SBrian King 	struct ibmvfc_event *evt;
4266ed830385SBrian King 
4267ed830385SBrian King 	kref_get(&tgt->kref);
4268e4b26f3dSTyrel Datwyler 	evt = ibmvfc_get_event(&vhost->crq);
4269*8bbe784cSTyrel Datwyler 	if (!evt)
4270*8bbe784cSTyrel Datwyler 		return NULL;
4271ed830385SBrian King 	ibmvfc_init_event(evt, done, IBMVFC_MAD_FORMAT);
4272ed830385SBrian King 	evt->tgt = tgt;
4273ed830385SBrian King 	mad = &evt->iu.implicit_logout;
4274ed830385SBrian King 	memset(mad, 0, sizeof(*mad));
4275ed830385SBrian King 	mad->common.version = cpu_to_be32(1);
4276ed830385SBrian King 	mad->common.opcode = cpu_to_be32(IBMVFC_IMPLICIT_LOGOUT);
4277ed830385SBrian King 	mad->common.length = cpu_to_be16(sizeof(*mad));
4278ed830385SBrian King 	mad->old_scsi_id = cpu_to_be64(tgt->scsi_id);
4279ed830385SBrian King 	return evt;
4280ed830385SBrian King }
4281ed830385SBrian King 
4282ed830385SBrian King /**
4283072b91f9SBrian King  * ibmvfc_tgt_implicit_logout - Initiate an Implicit Logout for specified target
4284072b91f9SBrian King  * @tgt:		ibmvfc target struct
4285072b91f9SBrian King  *
4286072b91f9SBrian King  **/
ibmvfc_tgt_implicit_logout(struct ibmvfc_target * tgt)4287072b91f9SBrian King static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt)
4288072b91f9SBrian King {
4289072b91f9SBrian King 	struct ibmvfc_host *vhost = tgt->vhost;
4290072b91f9SBrian King 	struct ibmvfc_event *evt;
4291072b91f9SBrian King 
4292072b91f9SBrian King 	if (vhost->discovery_threads >= disc_threads)
4293072b91f9SBrian King 		return;
4294072b91f9SBrian King 
4295072b91f9SBrian King 	vhost->discovery_threads++;
4296ed830385SBrian King 	evt = __ibmvfc_tgt_get_implicit_logout_evt(tgt,
4297ed830385SBrian King 						   ibmvfc_tgt_implicit_logout_done);
4298*8bbe784cSTyrel Datwyler 	if (!evt) {
4299*8bbe784cSTyrel Datwyler 		vhost->discovery_threads--;
4300*8bbe784cSTyrel Datwyler 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4301*8bbe784cSTyrel Datwyler 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4302*8bbe784cSTyrel Datwyler 		__ibmvfc_reset_host(vhost);
4303*8bbe784cSTyrel Datwyler 		return;
4304*8bbe784cSTyrel Datwyler 	}
4305072b91f9SBrian King 
4306072b91f9SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
4307072b91f9SBrian King 	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
4308072b91f9SBrian King 		vhost->discovery_threads--;
4309072b91f9SBrian King 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4310072b91f9SBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4311072b91f9SBrian King 	} else
4312072b91f9SBrian King 		tgt_dbg(tgt, "Sent Implicit Logout\n");
4313072b91f9SBrian King }
4314072b91f9SBrian King 
4315072b91f9SBrian King /**
4316ed830385SBrian King  * ibmvfc_tgt_implicit_logout_and_del_done - Completion handler for Implicit Logout MAD
4317ed830385SBrian King  * @evt:	ibmvfc event struct
4318ed830385SBrian King  *
4319ed830385SBrian King  **/
ibmvfc_tgt_implicit_logout_and_del_done(struct ibmvfc_event * evt)4320ed830385SBrian King static void ibmvfc_tgt_implicit_logout_and_del_done(struct ibmvfc_event *evt)
4321ed830385SBrian King {
4322ed830385SBrian King 	struct ibmvfc_target *tgt = evt->tgt;
4323ed830385SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
4324ed830385SBrian King 	struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru;
4325ed830385SBrian King 	u32 status = be16_to_cpu(mad->common.status);
4326ed830385SBrian King 
4327ed830385SBrian King 	vhost->discovery_threads--;
4328ed830385SBrian King 	ibmvfc_free_event(evt);
43294b29cb61SBrian King 
43304b29cb61SBrian King 	/*
43314b29cb61SBrian King 	 * If our state is IBMVFC_HOST_OFFLINE, we could be unloading the
43324b29cb61SBrian King 	 * driver in which case we need to free up all the targets. If we are
43334b29cb61SBrian King 	 * not unloading, we will still go through a hard reset to get out of
43344b29cb61SBrian King 	 * offline state, so there is no need to track the old targets in that
43354b29cb61SBrian King 	 * case.
43364b29cb61SBrian King 	 */
43374b29cb61SBrian King 	if (status == IBMVFC_MAD_SUCCESS || vhost->state == IBMVFC_HOST_OFFLINE)
4338ed830385SBrian King 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
43394b29cb61SBrian King 	else
43404b29cb61SBrian King 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT);
4341ed830385SBrian King 
4342ed830385SBrian King 	tgt_dbg(tgt, "Implicit Logout %s\n", (status == IBMVFC_MAD_SUCCESS) ? "succeeded" : "failed");
4343ed830385SBrian King 	kref_put(&tgt->kref, ibmvfc_release_tgt);
4344ed830385SBrian King 	wake_up(&vhost->work_wait_q);
4345ed830385SBrian King }
4346ed830385SBrian King 
4347ed830385SBrian King /**
4348ed830385SBrian King  * ibmvfc_tgt_implicit_logout_and_del - Initiate an Implicit Logout for specified target
4349ed830385SBrian King  * @tgt:		ibmvfc target struct
4350ed830385SBrian King  *
4351ed830385SBrian King  **/
ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target * tgt)4352ed830385SBrian King static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *tgt)
4353ed830385SBrian King {
4354ed830385SBrian King 	struct ibmvfc_host *vhost = tgt->vhost;
4355ed830385SBrian King 	struct ibmvfc_event *evt;
4356ed830385SBrian King 
435766bb7fa8SBrian King 	if (!vhost->logged_in) {
435866bb7fa8SBrian King 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
435966bb7fa8SBrian King 		return;
436066bb7fa8SBrian King 	}
436166bb7fa8SBrian King 
4362ed830385SBrian King 	if (vhost->discovery_threads >= disc_threads)
4363b893eb01SBrian King 		return;
4364ed830385SBrian King 
4365ed830385SBrian King 	vhost->discovery_threads++;
4366ed830385SBrian King 	evt = __ibmvfc_tgt_get_implicit_logout_evt(tgt,
4367ed830385SBrian King 						   ibmvfc_tgt_implicit_logout_and_del_done);
4368ed830385SBrian King 
4369ed830385SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT);
4370ed830385SBrian King 	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
4371ed830385SBrian King 		vhost->discovery_threads--;
4372ed830385SBrian King 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
4373ed830385SBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4374ed830385SBrian King 	} else
4375ed830385SBrian King 		tgt_dbg(tgt, "Sent Implicit Logout\n");
4376ed830385SBrian King }
4377ed830385SBrian King 
4378ed830385SBrian King /**
43794b29cb61SBrian King  * ibmvfc_tgt_move_login_done - Completion handler for Move Login
43804b29cb61SBrian King  * @evt:	ibmvfc event struct
43814b29cb61SBrian King  *
43824b29cb61SBrian King  **/
ibmvfc_tgt_move_login_done(struct ibmvfc_event * evt)43834b29cb61SBrian King static void ibmvfc_tgt_move_login_done(struct ibmvfc_event *evt)
43844b29cb61SBrian King {
43854b29cb61SBrian King 	struct ibmvfc_target *tgt = evt->tgt;
43864b29cb61SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
43874b29cb61SBrian King 	struct ibmvfc_move_login *rsp = &evt->xfer_iu->move_login;
43884b29cb61SBrian King 	u32 status = be16_to_cpu(rsp->common.status);
43894b29cb61SBrian King 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
43904b29cb61SBrian King 
43914b29cb61SBrian King 	vhost->discovery_threads--;
43924b29cb61SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
43934b29cb61SBrian King 	switch (status) {
43944b29cb61SBrian King 	case IBMVFC_MAD_SUCCESS:
4395d5b45dd5SBrian King 		tgt_dbg(tgt, "Move Login succeeded for new scsi_id: %llX\n", tgt->new_scsi_id);
43964b29cb61SBrian King 		tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name);
43974b29cb61SBrian King 		tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name);
4398d5b45dd5SBrian King 		tgt->scsi_id = tgt->new_scsi_id;
43994b29cb61SBrian King 		tgt->ids.port_id = tgt->scsi_id;
44004b29cb61SBrian King 		memcpy(&tgt->service_parms, &rsp->service_parms,
44014b29cb61SBrian King 		       sizeof(tgt->service_parms));
44024b29cb61SBrian King 		memcpy(&tgt->service_parms_change, &rsp->service_parms_change,
44034b29cb61SBrian King 		       sizeof(tgt->service_parms_change));
44044b29cb61SBrian King 		ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_prli);
44054b29cb61SBrian King 		break;
44064b29cb61SBrian King 	case IBMVFC_MAD_DRIVER_FAILED:
44074b29cb61SBrian King 		break;
44084b29cb61SBrian King 	case IBMVFC_MAD_CRQ_ERROR:
44094b29cb61SBrian King 		ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_move_login);
44104b29cb61SBrian King 		break;
44114b29cb61SBrian King 	case IBMVFC_MAD_FAILED:
44124b29cb61SBrian King 	default:
44134b29cb61SBrian King 		level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_move_login);
44144b29cb61SBrian King 
44154b29cb61SBrian King 		tgt_log(tgt, level,
4416d5b45dd5SBrian King 			"Move Login failed: new scsi_id: %llX, flags:%x, vios_flags:%x, rc=0x%02X\n",
4417d5b45dd5SBrian King 			tgt->new_scsi_id, be32_to_cpu(rsp->flags), be16_to_cpu(rsp->vios_flags),
44184b29cb61SBrian King 			status);
44194b29cb61SBrian King 		break;
44204b29cb61SBrian King 	}
44214b29cb61SBrian King 
44224b29cb61SBrian King 	kref_put(&tgt->kref, ibmvfc_release_tgt);
44234b29cb61SBrian King 	ibmvfc_free_event(evt);
44244b29cb61SBrian King 	wake_up(&vhost->work_wait_q);
44254b29cb61SBrian King }
44264b29cb61SBrian King 
44274b29cb61SBrian King 
44284b29cb61SBrian King /**
44294b29cb61SBrian King  * ibmvfc_tgt_move_login - Initiate a move login for specified target
44304b29cb61SBrian King  * @tgt:		ibmvfc target struct
44314b29cb61SBrian King  *
44324b29cb61SBrian King  **/
ibmvfc_tgt_move_login(struct ibmvfc_target * tgt)44334b29cb61SBrian King static void ibmvfc_tgt_move_login(struct ibmvfc_target *tgt)
44344b29cb61SBrian King {
44354b29cb61SBrian King 	struct ibmvfc_host *vhost = tgt->vhost;
44364b29cb61SBrian King 	struct ibmvfc_move_login *move;
44374b29cb61SBrian King 	struct ibmvfc_event *evt;
44384b29cb61SBrian King 
44394b29cb61SBrian King 	if (vhost->discovery_threads >= disc_threads)
44404b29cb61SBrian King 		return;
44414b29cb61SBrian King 
44424b29cb61SBrian King 	kref_get(&tgt->kref);
4443e4b26f3dSTyrel Datwyler 	evt = ibmvfc_get_event(&vhost->crq);
4444*8bbe784cSTyrel Datwyler 	if (!evt) {
4445*8bbe784cSTyrel Datwyler 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
4446*8bbe784cSTyrel Datwyler 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4447*8bbe784cSTyrel Datwyler 		__ibmvfc_reset_host(vhost);
4448*8bbe784cSTyrel Datwyler 		return;
4449*8bbe784cSTyrel Datwyler 	}
44504b29cb61SBrian King 	vhost->discovery_threads++;
44514b29cb61SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
44524b29cb61SBrian King 	ibmvfc_init_event(evt, ibmvfc_tgt_move_login_done, IBMVFC_MAD_FORMAT);
44534b29cb61SBrian King 	evt->tgt = tgt;
44544b29cb61SBrian King 	move = &evt->iu.move_login;
44554b29cb61SBrian King 	memset(move, 0, sizeof(*move));
44564b29cb61SBrian King 	move->common.version = cpu_to_be32(1);
44574b29cb61SBrian King 	move->common.opcode = cpu_to_be32(IBMVFC_MOVE_LOGIN);
44584b29cb61SBrian King 	move->common.length = cpu_to_be16(sizeof(*move));
44594b29cb61SBrian King 
4460d5b45dd5SBrian King 	move->old_scsi_id = cpu_to_be64(tgt->scsi_id);
4461d5b45dd5SBrian King 	move->new_scsi_id = cpu_to_be64(tgt->new_scsi_id);
44624b29cb61SBrian King 	move->wwpn = cpu_to_be64(tgt->wwpn);
44634b29cb61SBrian King 	move->node_name = cpu_to_be64(tgt->ids.node_name);
44644b29cb61SBrian King 
44654b29cb61SBrian King 	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
44664b29cb61SBrian King 		vhost->discovery_threads--;
44674b29cb61SBrian King 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
44684b29cb61SBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
44694b29cb61SBrian King 	} else
4470d5b45dd5SBrian King 		tgt_dbg(tgt, "Sent Move Login for new scsi_id: %llX\n", tgt->new_scsi_id);
44714b29cb61SBrian King }
44724b29cb61SBrian King 
44734b29cb61SBrian King /**
4474989b8545SBrian King  * ibmvfc_adisc_needs_plogi - Does device need PLOGI?
4475989b8545SBrian King  * @mad:	ibmvfc passthru mad struct
4476989b8545SBrian King  * @tgt:	ibmvfc target struct
4477989b8545SBrian King  *
4478989b8545SBrian King  * Returns:
4479989b8545SBrian King  *	1 if PLOGI needed / 0 if PLOGI not needed
4480989b8545SBrian King  **/
ibmvfc_adisc_needs_plogi(struct ibmvfc_passthru_mad * mad,struct ibmvfc_target * tgt)4481989b8545SBrian King static int ibmvfc_adisc_needs_plogi(struct ibmvfc_passthru_mad *mad,
4482989b8545SBrian King 				    struct ibmvfc_target *tgt)
4483989b8545SBrian King {
448409dd15e0SBrian King 	if (wwn_to_u64((u8 *)&mad->fc_iu.response[2]) != tgt->ids.port_name)
4485989b8545SBrian King 		return 1;
448609dd15e0SBrian King 	if (wwn_to_u64((u8 *)&mad->fc_iu.response[4]) != tgt->ids.node_name)
4487989b8545SBrian King 		return 1;
44880aab6c3fSTyrel Datwyler 	if (be32_to_cpu(mad->fc_iu.response[6]) != tgt->scsi_id)
4489989b8545SBrian King 		return 1;
4490989b8545SBrian King 	return 0;
4491989b8545SBrian King }
4492989b8545SBrian King 
4493989b8545SBrian King /**
4494989b8545SBrian King  * ibmvfc_tgt_adisc_done - Completion handler for ADISC
4495989b8545SBrian King  * @evt:	ibmvfc event struct
4496989b8545SBrian King  *
4497989b8545SBrian King  **/
ibmvfc_tgt_adisc_done(struct ibmvfc_event * evt)4498989b8545SBrian King static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt)
4499989b8545SBrian King {
4500989b8545SBrian King 	struct ibmvfc_target *tgt = evt->tgt;
4501989b8545SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
4502989b8545SBrian King 	struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru;
45030aab6c3fSTyrel Datwyler 	u32 status = be16_to_cpu(mad->common.status);
4504989b8545SBrian King 	u8 fc_reason, fc_explain;
4505989b8545SBrian King 
4506989b8545SBrian King 	vhost->discovery_threads--;
4507989b8545SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
450810501e1cSBrian King 	del_timer(&tgt->timer);
4509989b8545SBrian King 
4510989b8545SBrian King 	switch (status) {
4511989b8545SBrian King 	case IBMVFC_MAD_SUCCESS:
4512989b8545SBrian King 		tgt_dbg(tgt, "ADISC succeeded\n");
4513989b8545SBrian King 		if (ibmvfc_adisc_needs_plogi(mad, tgt))
4514ed830385SBrian King 			ibmvfc_del_tgt(tgt);
4515989b8545SBrian King 		break;
4516989b8545SBrian King 	case IBMVFC_MAD_DRIVER_FAILED:
4517989b8545SBrian King 		break;
4518989b8545SBrian King 	case IBMVFC_MAD_FAILED:
4519989b8545SBrian King 	default:
4520ed830385SBrian King 		ibmvfc_del_tgt(tgt);
45210aab6c3fSTyrel Datwyler 		fc_reason = (be32_to_cpu(mad->fc_iu.response[1]) & 0x00ff0000) >> 16;
45220aab6c3fSTyrel Datwyler 		fc_explain = (be32_to_cpu(mad->fc_iu.response[1]) & 0x0000ff00) >> 8;
4523989b8545SBrian King 		tgt_info(tgt, "ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
45240aab6c3fSTyrel Datwyler 			 ibmvfc_get_cmd_error(be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error)),
45253e6f7de4STyrel Datwyler 			 be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error),
4526989b8545SBrian King 			 ibmvfc_get_fc_type(fc_reason), fc_reason,
4527989b8545SBrian King 			 ibmvfc_get_ls_explain(fc_explain), fc_explain, status);
4528989b8545SBrian King 		break;
4529f36cfe6aSChristopher Díaz Riveros 	}
4530989b8545SBrian King 
4531989b8545SBrian King 	kref_put(&tgt->kref, ibmvfc_release_tgt);
4532989b8545SBrian King 	ibmvfc_free_event(evt);
4533989b8545SBrian King 	wake_up(&vhost->work_wait_q);
4534989b8545SBrian King }
4535989b8545SBrian King 
4536989b8545SBrian King /**
4537989b8545SBrian King  * ibmvfc_init_passthru - Initialize an event struct for FC passthru
4538989b8545SBrian King  * @evt:		ibmvfc event struct
4539989b8545SBrian King  *
4540989b8545SBrian King  **/
ibmvfc_init_passthru(struct ibmvfc_event * evt)4541989b8545SBrian King static void ibmvfc_init_passthru(struct ibmvfc_event *evt)
4542989b8545SBrian King {
4543989b8545SBrian King 	struct ibmvfc_passthru_mad *mad = &evt->iu.passthru;
4544989b8545SBrian King 
4545989b8545SBrian King 	memset(mad, 0, sizeof(*mad));
45460aab6c3fSTyrel Datwyler 	mad->common.version = cpu_to_be32(1);
45470aab6c3fSTyrel Datwyler 	mad->common.opcode = cpu_to_be32(IBMVFC_PASSTHRU);
45480aab6c3fSTyrel Datwyler 	mad->common.length = cpu_to_be16(sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu));
45490aab6c3fSTyrel Datwyler 	mad->cmd_ioba.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) +
45500aab6c3fSTyrel Datwyler 		offsetof(struct ibmvfc_passthru_mad, iu));
45510aab6c3fSTyrel Datwyler 	mad->cmd_ioba.len = cpu_to_be32(sizeof(mad->iu));
45520aab6c3fSTyrel Datwyler 	mad->iu.cmd_len = cpu_to_be32(sizeof(mad->fc_iu.payload));
45530aab6c3fSTyrel Datwyler 	mad->iu.rsp_len = cpu_to_be32(sizeof(mad->fc_iu.response));
45540aab6c3fSTyrel Datwyler 	mad->iu.cmd.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) +
4555989b8545SBrian King 		offsetof(struct ibmvfc_passthru_mad, fc_iu) +
45560aab6c3fSTyrel Datwyler 		offsetof(struct ibmvfc_passthru_fc_iu, payload));
45570aab6c3fSTyrel Datwyler 	mad->iu.cmd.len = cpu_to_be32(sizeof(mad->fc_iu.payload));
45580aab6c3fSTyrel Datwyler 	mad->iu.rsp.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) +
4559989b8545SBrian King 		offsetof(struct ibmvfc_passthru_mad, fc_iu) +
45600aab6c3fSTyrel Datwyler 		offsetof(struct ibmvfc_passthru_fc_iu, response));
45610aab6c3fSTyrel Datwyler 	mad->iu.rsp.len = cpu_to_be32(sizeof(mad->fc_iu.response));
4562989b8545SBrian King }
4563989b8545SBrian King 
4564989b8545SBrian King /**
456510501e1cSBrian King  * ibmvfc_tgt_adisc_cancel_done - Completion handler when cancelling an ADISC
456610501e1cSBrian King  * @evt:		ibmvfc event struct
456710501e1cSBrian King  *
456810501e1cSBrian King  * Just cleanup this event struct. Everything else is handled by
456910501e1cSBrian King  * the ADISC completion handler. If the ADISC never actually comes
457010501e1cSBrian King  * back, we still have the timer running on the ADISC event struct
457110501e1cSBrian King  * which will fire and cause the CRQ to get reset.
457210501e1cSBrian King  *
457310501e1cSBrian King  **/
ibmvfc_tgt_adisc_cancel_done(struct ibmvfc_event * evt)457410501e1cSBrian King static void ibmvfc_tgt_adisc_cancel_done(struct ibmvfc_event *evt)
457510501e1cSBrian King {
457610501e1cSBrian King 	struct ibmvfc_host *vhost = evt->vhost;
457710501e1cSBrian King 	struct ibmvfc_target *tgt = evt->tgt;
457810501e1cSBrian King 
457910501e1cSBrian King 	tgt_dbg(tgt, "ADISC cancel complete\n");
458010501e1cSBrian King 	vhost->abort_threads--;
458110501e1cSBrian King 	ibmvfc_free_event(evt);
458210501e1cSBrian King 	kref_put(&tgt->kref, ibmvfc_release_tgt);
458310501e1cSBrian King 	wake_up(&vhost->work_wait_q);
458410501e1cSBrian King }
458510501e1cSBrian King 
458610501e1cSBrian King /**
458710501e1cSBrian King  * ibmvfc_adisc_timeout - Handle an ADISC timeout
4588dd9c7729SLee Jones  * @t:		ibmvfc target struct
458910501e1cSBrian King  *
459010501e1cSBrian King  * If an ADISC times out, send a cancel. If the cancel times
459110501e1cSBrian King  * out, reset the CRQ. When the ADISC comes back as cancelled,
459210501e1cSBrian King  * log back into the target.
459310501e1cSBrian King  **/
ibmvfc_adisc_timeout(struct timer_list * t)45949a5d04fcSKees Cook static void ibmvfc_adisc_timeout(struct timer_list *t)
459510501e1cSBrian King {
45969a5d04fcSKees Cook 	struct ibmvfc_target *tgt = from_timer(tgt, t, timer);
459710501e1cSBrian King 	struct ibmvfc_host *vhost = tgt->vhost;
459810501e1cSBrian King 	struct ibmvfc_event *evt;
459910501e1cSBrian King 	struct ibmvfc_tmf *tmf;
460010501e1cSBrian King 	unsigned long flags;
460110501e1cSBrian King 	int rc;
460210501e1cSBrian King 
460310501e1cSBrian King 	tgt_dbg(tgt, "ADISC timeout\n");
460410501e1cSBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
460510501e1cSBrian King 	if (vhost->abort_threads >= disc_threads ||
460610501e1cSBrian King 	    tgt->action != IBMVFC_TGT_ACTION_INIT_WAIT ||
460710501e1cSBrian King 	    vhost->state != IBMVFC_INITIALIZING ||
460810501e1cSBrian King 	    vhost->action != IBMVFC_HOST_ACTION_QUERY_TGTS) {
460910501e1cSBrian King 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
461010501e1cSBrian King 		return;
461110501e1cSBrian King 	}
461210501e1cSBrian King 
461310501e1cSBrian King 	vhost->abort_threads++;
461410501e1cSBrian King 	kref_get(&tgt->kref);
4615e4b26f3dSTyrel Datwyler 	evt = ibmvfc_get_event(&vhost->crq);
4616*8bbe784cSTyrel Datwyler 	if (!evt) {
4617*8bbe784cSTyrel Datwyler 		tgt_err(tgt, "Failed to get cancel event for ADISC.\n");
4618*8bbe784cSTyrel Datwyler 		vhost->abort_threads--;
4619*8bbe784cSTyrel Datwyler 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4620*8bbe784cSTyrel Datwyler 		__ibmvfc_reset_host(vhost);
4621*8bbe784cSTyrel Datwyler 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
4622*8bbe784cSTyrel Datwyler 		return;
4623*8bbe784cSTyrel Datwyler 	}
462410501e1cSBrian King 	ibmvfc_init_event(evt, ibmvfc_tgt_adisc_cancel_done, IBMVFC_MAD_FORMAT);
462510501e1cSBrian King 
462610501e1cSBrian King 	evt->tgt = tgt;
462710501e1cSBrian King 	tmf = &evt->iu.tmf;
462810501e1cSBrian King 	memset(tmf, 0, sizeof(*tmf));
4629ebc7c74bSTyrel Datwyler 	if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
4630ebc7c74bSTyrel Datwyler 		tmf->common.version = cpu_to_be32(2);
4631ebc7c74bSTyrel Datwyler 		tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
4632ebc7c74bSTyrel Datwyler 	} else {
46330aab6c3fSTyrel Datwyler 		tmf->common.version = cpu_to_be32(1);
4634ebc7c74bSTyrel Datwyler 	}
46350aab6c3fSTyrel Datwyler 	tmf->common.opcode = cpu_to_be32(IBMVFC_TMF_MAD);
46360aab6c3fSTyrel Datwyler 	tmf->common.length = cpu_to_be16(sizeof(*tmf));
46370aab6c3fSTyrel Datwyler 	tmf->scsi_id = cpu_to_be64(tgt->scsi_id);
46380aab6c3fSTyrel Datwyler 	tmf->cancel_key = cpu_to_be32(tgt->cancel_key);
463910501e1cSBrian King 
464010501e1cSBrian King 	rc = ibmvfc_send_event(evt, vhost, default_timeout);
464110501e1cSBrian King 
464210501e1cSBrian King 	if (rc) {
464310501e1cSBrian King 		tgt_err(tgt, "Failed to send cancel event for ADISC. rc=%d\n", rc);
464410501e1cSBrian King 		vhost->abort_threads--;
464510501e1cSBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
464610501e1cSBrian King 		__ibmvfc_reset_host(vhost);
464710501e1cSBrian King 	} else
464810501e1cSBrian King 		tgt_dbg(tgt, "Attempting to cancel ADISC\n");
464910501e1cSBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
465010501e1cSBrian King }
465110501e1cSBrian King 
465210501e1cSBrian King /**
4653989b8545SBrian King  * ibmvfc_tgt_adisc - Initiate an ADISC for specified target
4654989b8545SBrian King  * @tgt:		ibmvfc target struct
4655989b8545SBrian King  *
465610501e1cSBrian King  * When sending an ADISC we end up with two timers running. The
465710501e1cSBrian King  * first timer is the timer in the ibmvfc target struct. If this
465810501e1cSBrian King  * fires, we send a cancel to the target. The second timer is the
465910501e1cSBrian King  * timer on the ibmvfc event for the ADISC, which is longer. If that
466010501e1cSBrian King  * fires, it means the ADISC timed out and our attempt to cancel it
466110501e1cSBrian King  * also failed, so we need to reset the CRQ.
4662989b8545SBrian King  **/
ibmvfc_tgt_adisc(struct ibmvfc_target * tgt)4663989b8545SBrian King static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt)
4664989b8545SBrian King {
4665989b8545SBrian King 	struct ibmvfc_passthru_mad *mad;
4666989b8545SBrian King 	struct ibmvfc_host *vhost = tgt->vhost;
4667989b8545SBrian King 	struct ibmvfc_event *evt;
4668989b8545SBrian King 
4669989b8545SBrian King 	if (vhost->discovery_threads >= disc_threads)
4670989b8545SBrian King 		return;
4671989b8545SBrian King 
4672989b8545SBrian King 	kref_get(&tgt->kref);
4673e4b26f3dSTyrel Datwyler 	evt = ibmvfc_get_event(&vhost->crq);
4674*8bbe784cSTyrel Datwyler 	if (!evt) {
4675*8bbe784cSTyrel Datwyler 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4676*8bbe784cSTyrel Datwyler 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4677*8bbe784cSTyrel Datwyler 		__ibmvfc_reset_host(vhost);
4678*8bbe784cSTyrel Datwyler 		return;
4679*8bbe784cSTyrel Datwyler 	}
4680989b8545SBrian King 	vhost->discovery_threads++;
4681989b8545SBrian King 	ibmvfc_init_event(evt, ibmvfc_tgt_adisc_done, IBMVFC_MAD_FORMAT);
4682989b8545SBrian King 	evt->tgt = tgt;
4683989b8545SBrian King 
4684989b8545SBrian King 	ibmvfc_init_passthru(evt);
4685989b8545SBrian King 	mad = &evt->iu.passthru;
46860aab6c3fSTyrel Datwyler 	mad->iu.flags = cpu_to_be32(IBMVFC_FC_ELS);
46870aab6c3fSTyrel Datwyler 	mad->iu.scsi_id = cpu_to_be64(tgt->scsi_id);
46880aab6c3fSTyrel Datwyler 	mad->iu.cancel_key = cpu_to_be32(tgt->cancel_key);
4689989b8545SBrian King 
46900aab6c3fSTyrel Datwyler 	mad->fc_iu.payload[0] = cpu_to_be32(IBMVFC_ADISC);
4691989b8545SBrian King 	memcpy(&mad->fc_iu.payload[2], &vhost->login_buf->resp.port_name,
4692989b8545SBrian King 	       sizeof(vhost->login_buf->resp.port_name));
4693989b8545SBrian King 	memcpy(&mad->fc_iu.payload[4], &vhost->login_buf->resp.node_name,
4694989b8545SBrian King 	       sizeof(vhost->login_buf->resp.node_name));
46950aab6c3fSTyrel Datwyler 	mad->fc_iu.payload[6] = cpu_to_be32(be64_to_cpu(vhost->login_buf->resp.scsi_id) & 0x00ffffff);
4696989b8545SBrian King 
469710501e1cSBrian King 	if (timer_pending(&tgt->timer))
469810501e1cSBrian King 		mod_timer(&tgt->timer, jiffies + (IBMVFC_ADISC_TIMEOUT * HZ));
469910501e1cSBrian King 	else {
470010501e1cSBrian King 		tgt->timer.expires = jiffies + (IBMVFC_ADISC_TIMEOUT * HZ);
470110501e1cSBrian King 		add_timer(&tgt->timer);
470210501e1cSBrian King 	}
470310501e1cSBrian King 
4704989b8545SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
470510501e1cSBrian King 	if (ibmvfc_send_event(evt, vhost, IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT)) {
4706989b8545SBrian King 		vhost->discovery_threads--;
470710501e1cSBrian King 		del_timer(&tgt->timer);
4708989b8545SBrian King 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4709989b8545SBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4710989b8545SBrian King 	} else
4711989b8545SBrian King 		tgt_dbg(tgt, "Sent ADISC\n");
4712989b8545SBrian King }
4713989b8545SBrian King 
4714989b8545SBrian King /**
4715072b91f9SBrian King  * ibmvfc_tgt_query_target_done - Completion handler for Query Target MAD
4716072b91f9SBrian King  * @evt:	ibmvfc event struct
4717072b91f9SBrian King  *
4718072b91f9SBrian King  **/
ibmvfc_tgt_query_target_done(struct ibmvfc_event * evt)4719072b91f9SBrian King static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
4720072b91f9SBrian King {
4721072b91f9SBrian King 	struct ibmvfc_target *tgt = evt->tgt;
4722072b91f9SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
4723072b91f9SBrian King 	struct ibmvfc_query_tgt *rsp = &evt->xfer_iu->query_tgt;
47240aab6c3fSTyrel Datwyler 	u32 status = be16_to_cpu(rsp->common.status);
47257d0e4622SBrian King 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
4726072b91f9SBrian King 
4727072b91f9SBrian King 	vhost->discovery_threads--;
4728072b91f9SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4729072b91f9SBrian King 	switch (status) {
4730072b91f9SBrian King 	case IBMVFC_MAD_SUCCESS:
4731072b91f9SBrian King 		tgt_dbg(tgt, "Query Target succeeded\n");
47320aab6c3fSTyrel Datwyler 		if (be64_to_cpu(rsp->scsi_id) != tgt->scsi_id)
4733ed830385SBrian King 			ibmvfc_del_tgt(tgt);
4734989b8545SBrian King 		else
4735989b8545SBrian King 			ibmvfc_init_tgt(tgt, ibmvfc_tgt_adisc);
4736072b91f9SBrian King 		break;
4737072b91f9SBrian King 	case IBMVFC_MAD_DRIVER_FAILED:
4738072b91f9SBrian King 		break;
4739072b91f9SBrian King 	case IBMVFC_MAD_CRQ_ERROR:
4740072b91f9SBrian King 		ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
4741072b91f9SBrian King 		break;
4742072b91f9SBrian King 	case IBMVFC_MAD_FAILED:
4743072b91f9SBrian King 	default:
47440aab6c3fSTyrel Datwyler 		if ((be16_to_cpu(rsp->status) & IBMVFC_FABRIC_MAPPED) == IBMVFC_FABRIC_MAPPED &&
47450aab6c3fSTyrel Datwyler 		    be16_to_cpu(rsp->error) == IBMVFC_UNABLE_TO_PERFORM_REQ &&
47460aab6c3fSTyrel Datwyler 		    be16_to_cpu(rsp->fc_explain) == IBMVFC_PORT_NAME_NOT_REG)
4747ed830385SBrian King 			ibmvfc_del_tgt(tgt);
47480aab6c3fSTyrel Datwyler 		else if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
47497d0e4622SBrian King 			level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
475010e79499SBrian King 		else
4751ed830385SBrian King 			ibmvfc_del_tgt(tgt);
47527d0e4622SBrian King 
47537d0e4622SBrian King 		tgt_log(tgt, level, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
47540aab6c3fSTyrel Datwyler 			ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
47553e6f7de4STyrel Datwyler 			be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
47563e6f7de4STyrel Datwyler 			ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type),
47573e6f7de4STyrel Datwyler 			ibmvfc_get_gs_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain),
47583e6f7de4STyrel Datwyler 			status);
4759072b91f9SBrian King 		break;
4760f36cfe6aSChristopher Díaz Riveros 	}
4761072b91f9SBrian King 
4762072b91f9SBrian King 	kref_put(&tgt->kref, ibmvfc_release_tgt);
4763072b91f9SBrian King 	ibmvfc_free_event(evt);
4764072b91f9SBrian King 	wake_up(&vhost->work_wait_q);
4765072b91f9SBrian King }
4766072b91f9SBrian King 
4767072b91f9SBrian King /**
4768072b91f9SBrian King  * ibmvfc_tgt_query_target - Initiate a Query Target for specified target
4769072b91f9SBrian King  * @tgt:	ibmvfc target struct
4770072b91f9SBrian King  *
4771072b91f9SBrian King  **/
ibmvfc_tgt_query_target(struct ibmvfc_target * tgt)4772072b91f9SBrian King static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
4773072b91f9SBrian King {
4774072b91f9SBrian King 	struct ibmvfc_query_tgt *query_tgt;
4775072b91f9SBrian King 	struct ibmvfc_host *vhost = tgt->vhost;
4776072b91f9SBrian King 	struct ibmvfc_event *evt;
4777072b91f9SBrian King 
4778072b91f9SBrian King 	if (vhost->discovery_threads >= disc_threads)
4779072b91f9SBrian King 		return;
4780072b91f9SBrian King 
4781072b91f9SBrian King 	kref_get(&tgt->kref);
4782e4b26f3dSTyrel Datwyler 	evt = ibmvfc_get_event(&vhost->crq);
4783*8bbe784cSTyrel Datwyler 	if (!evt) {
4784*8bbe784cSTyrel Datwyler 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4785*8bbe784cSTyrel Datwyler 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4786*8bbe784cSTyrel Datwyler 		__ibmvfc_reset_host(vhost);
4787*8bbe784cSTyrel Datwyler 		return;
4788*8bbe784cSTyrel Datwyler 	}
4789072b91f9SBrian King 	vhost->discovery_threads++;
4790072b91f9SBrian King 	evt->tgt = tgt;
4791072b91f9SBrian King 	ibmvfc_init_event(evt, ibmvfc_tgt_query_target_done, IBMVFC_MAD_FORMAT);
4792072b91f9SBrian King 	query_tgt = &evt->iu.query_tgt;
4793072b91f9SBrian King 	memset(query_tgt, 0, sizeof(*query_tgt));
47940aab6c3fSTyrel Datwyler 	query_tgt->common.version = cpu_to_be32(1);
47950aab6c3fSTyrel Datwyler 	query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET);
47960aab6c3fSTyrel Datwyler 	query_tgt->common.length = cpu_to_be16(sizeof(*query_tgt));
47970aab6c3fSTyrel Datwyler 	query_tgt->wwpn = cpu_to_be64(tgt->ids.port_name);
4798072b91f9SBrian King 
4799072b91f9SBrian King 	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
4800072b91f9SBrian King 	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
4801072b91f9SBrian King 		vhost->discovery_threads--;
4802072b91f9SBrian King 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
4803072b91f9SBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
4804072b91f9SBrian King 	} else
4805072b91f9SBrian King 		tgt_dbg(tgt, "Sent Query Target\n");
4806072b91f9SBrian King }
4807072b91f9SBrian King 
4808072b91f9SBrian King /**
4809072b91f9SBrian King  * ibmvfc_alloc_target - Allocate and initialize an ibmvfc target
4810072b91f9SBrian King  * @vhost:		ibmvfc host struct
4811dd9c7729SLee Jones  * @target:		Holds SCSI ID to allocate target forand the WWPN
4812072b91f9SBrian King  *
4813072b91f9SBrian King  * Returns:
4814072b91f9SBrian King  *	0 on success / other on failure
4815072b91f9SBrian King  **/
ibmvfc_alloc_target(struct ibmvfc_host * vhost,struct ibmvfc_discover_targets_entry * target)48164b29cb61SBrian King static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
48174b29cb61SBrian King 			       struct ibmvfc_discover_targets_entry *target)
4818072b91f9SBrian King {
48194b29cb61SBrian King 	struct ibmvfc_target *stgt = NULL;
48204b29cb61SBrian King 	struct ibmvfc_target *wtgt = NULL;
4821072b91f9SBrian King 	struct ibmvfc_target *tgt;
4822072b91f9SBrian King 	unsigned long flags;
48234b29cb61SBrian King 	u64 scsi_id = be32_to_cpu(target->scsi_id) & IBMVFC_DISC_TGT_SCSI_ID_MASK;
48244b29cb61SBrian King 	u64 wwpn = be64_to_cpu(target->wwpn);
4825072b91f9SBrian King 
48264b29cb61SBrian King 	/* Look to see if we already have a target allocated for this SCSI ID or WWPN */
4827072b91f9SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
4828072b91f9SBrian King 	list_for_each_entry(tgt, &vhost->targets, queue) {
48294b29cb61SBrian King 		if (tgt->wwpn == wwpn) {
48304b29cb61SBrian King 			wtgt = tgt;
48314b29cb61SBrian King 			break;
48324b29cb61SBrian King 		}
48334b29cb61SBrian King 	}
48344b29cb61SBrian King 
48354b29cb61SBrian King 	list_for_each_entry(tgt, &vhost->targets, queue) {
4836072b91f9SBrian King 		if (tgt->scsi_id == scsi_id) {
48374b29cb61SBrian King 			stgt = tgt;
48384b29cb61SBrian King 			break;
48394b29cb61SBrian King 		}
48404b29cb61SBrian King 	}
48414b29cb61SBrian King 
48424b29cb61SBrian King 	if (wtgt && !stgt) {
48434b29cb61SBrian King 		/*
48444b29cb61SBrian King 		 * A WWPN target has moved and we still are tracking the old
48454b29cb61SBrian King 		 * SCSI ID.  The only way we should be able to get here is if
48464b29cb61SBrian King 		 * we attempted to send an implicit logout for the old SCSI ID
48474b29cb61SBrian King 		 * and it failed for some reason, such as there being I/O
48484b29cb61SBrian King 		 * pending to the target. In this case, we will have already
48494b29cb61SBrian King 		 * deleted the rport from the FC transport so we do a move
48505114975eSBrian King 		 * login, which works even with I/O pending, however, if
48515114975eSBrian King 		 * there is still I/O pending, it will stay outstanding, so
48525114975eSBrian King 		 * we only do this if fast fail is disabled for the rport,
48535114975eSBrian King 		 * otherwise we let terminate_rport_io clean up the port
48545114975eSBrian King 		 * before we login at the new location.
48554b29cb61SBrian King 		 */
48564b29cb61SBrian King 		if (wtgt->action == IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT) {
48575114975eSBrian King 			if (wtgt->move_login) {
48584b29cb61SBrian King 				/*
48594b29cb61SBrian King 				 * Do a move login here. The old target is no longer
48604b29cb61SBrian King 				 * known to the transport layer We don't use the
48614b29cb61SBrian King 				 * normal ibmvfc_set_tgt_action to set this, as we
48624b29cb61SBrian King 				 * don't normally want to allow this state change.
48634b29cb61SBrian King 				 */
4864d5b45dd5SBrian King 				wtgt->new_scsi_id = scsi_id;
48654b29cb61SBrian King 				wtgt->action = IBMVFC_TGT_ACTION_INIT;
48662e51f78bSBrian King 				wtgt->init_retries = 0;
48674b29cb61SBrian King 				ibmvfc_init_tgt(wtgt, ibmvfc_tgt_move_login);
48685114975eSBrian King 			}
48694b29cb61SBrian King 			goto unlock_out;
48704b29cb61SBrian King 		} else {
48714b29cb61SBrian King 			tgt_err(wtgt, "Unexpected target state: %d, %p\n",
48724b29cb61SBrian King 				wtgt->action, wtgt->rport);
48734b29cb61SBrian King 		}
48744b29cb61SBrian King 	} else if (stgt) {
4875072b91f9SBrian King 		if (tgt->need_login)
4876072b91f9SBrian King 			ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
4877072b91f9SBrian King 		goto unlock_out;
4878072b91f9SBrian King 	}
4879072b91f9SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
4880072b91f9SBrian King 
48817270b9bdSBrian King 	tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO);
48820883e3b3SBrian King 	memset(tgt, 0, sizeof(*tgt));
4883072b91f9SBrian King 	tgt->scsi_id = scsi_id;
48844b29cb61SBrian King 	tgt->wwpn = wwpn;
4885072b91f9SBrian King 	tgt->vhost = vhost;
4886072b91f9SBrian King 	tgt->need_login = 1;
48879a5d04fcSKees Cook 	timer_setup(&tgt->timer, ibmvfc_adisc_timeout, 0);
4888072b91f9SBrian King 	kref_init(&tgt->kref);
4889072b91f9SBrian King 	ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
4890072b91f9SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
48912584e5aeSBrian King 	tgt->cancel_key = vhost->task_set++;
4892072b91f9SBrian King 	list_add_tail(&tgt->queue, &vhost->targets);
4893072b91f9SBrian King 
4894072b91f9SBrian King unlock_out:
4895072b91f9SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
4896072b91f9SBrian King 	return 0;
4897072b91f9SBrian King }
4898072b91f9SBrian King 
4899072b91f9SBrian King /**
4900072b91f9SBrian King  * ibmvfc_alloc_targets - Allocate and initialize ibmvfc targets
4901072b91f9SBrian King  * @vhost:		ibmvfc host struct
4902072b91f9SBrian King  *
4903072b91f9SBrian King  * Returns:
4904072b91f9SBrian King  *	0 on success / other on failure
4905072b91f9SBrian King  **/
ibmvfc_alloc_targets(struct ibmvfc_host * vhost)4906072b91f9SBrian King static int ibmvfc_alloc_targets(struct ibmvfc_host *vhost)
4907072b91f9SBrian King {
4908072b91f9SBrian King 	int i, rc;
4909072b91f9SBrian King 
4910072b91f9SBrian King 	for (i = 0, rc = 0; !rc && i < vhost->num_targets; i++)
49114b29cb61SBrian King 		rc = ibmvfc_alloc_target(vhost, &vhost->disc_buf[i]);
4912072b91f9SBrian King 
4913072b91f9SBrian King 	return rc;
4914072b91f9SBrian King }
4915072b91f9SBrian King 
4916072b91f9SBrian King /**
4917072b91f9SBrian King  * ibmvfc_discover_targets_done - Completion handler for discover targets MAD
4918072b91f9SBrian King  * @evt:	ibmvfc event struct
4919072b91f9SBrian King  *
4920072b91f9SBrian King  **/
ibmvfc_discover_targets_done(struct ibmvfc_event * evt)4921072b91f9SBrian King static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
4922072b91f9SBrian King {
4923072b91f9SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
4924072b91f9SBrian King 	struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets;
49250aab6c3fSTyrel Datwyler 	u32 mad_status = be16_to_cpu(rsp->common.status);
49267d0e4622SBrian King 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
4927072b91f9SBrian King 
4928072b91f9SBrian King 	switch (mad_status) {
4929072b91f9SBrian King 	case IBMVFC_MAD_SUCCESS:
4930072b91f9SBrian King 		ibmvfc_dbg(vhost, "Discover Targets succeeded\n");
49310aab6c3fSTyrel Datwyler 		vhost->num_targets = be32_to_cpu(rsp->num_written);
4932072b91f9SBrian King 		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
4933072b91f9SBrian King 		break;
4934072b91f9SBrian King 	case IBMVFC_MAD_FAILED:
49357d0e4622SBrian King 		level += ibmvfc_retry_host_init(vhost);
49367d0e4622SBrian King 		ibmvfc_log(vhost, level, "Discover Targets failed: %s (%x:%x)\n",
49370aab6c3fSTyrel Datwyler 			   ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
49383e6f7de4STyrel Datwyler 			   be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
4939072b91f9SBrian King 		break;
4940072b91f9SBrian King 	case IBMVFC_MAD_DRIVER_FAILED:
4941072b91f9SBrian King 		break;
4942072b91f9SBrian King 	default:
4943072b91f9SBrian King 		dev_err(vhost->dev, "Invalid Discover Targets response: 0x%x\n", mad_status);
4944072b91f9SBrian King 		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
4945072b91f9SBrian King 		break;
4946072b91f9SBrian King 	}
4947072b91f9SBrian King 
4948072b91f9SBrian King 	ibmvfc_free_event(evt);
4949072b91f9SBrian King 	wake_up(&vhost->work_wait_q);
4950072b91f9SBrian King }
4951072b91f9SBrian King 
4952072b91f9SBrian King /**
4953072b91f9SBrian King  * ibmvfc_discover_targets - Send Discover Targets MAD
4954072b91f9SBrian King  * @vhost:	ibmvfc host struct
4955072b91f9SBrian King  *
4956072b91f9SBrian King  **/
ibmvfc_discover_targets(struct ibmvfc_host * vhost)4957072b91f9SBrian King static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
4958072b91f9SBrian King {
4959072b91f9SBrian King 	struct ibmvfc_discover_targets *mad;
4960e4b26f3dSTyrel Datwyler 	struct ibmvfc_event *evt = ibmvfc_get_event(&vhost->crq);
4961*8bbe784cSTyrel Datwyler 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
4962*8bbe784cSTyrel Datwyler 
4963*8bbe784cSTyrel Datwyler 	if (!evt) {
4964*8bbe784cSTyrel Datwyler 		ibmvfc_log(vhost, level, "Discover Targets failed: no available events\n");
4965*8bbe784cSTyrel Datwyler 		ibmvfc_hard_reset_host(vhost);
4966*8bbe784cSTyrel Datwyler 		return;
4967*8bbe784cSTyrel Datwyler 	}
4968072b91f9SBrian King 
4969072b91f9SBrian King 	ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT);
4970072b91f9SBrian King 	mad = &evt->iu.discover_targets;
4971072b91f9SBrian King 	memset(mad, 0, sizeof(*mad));
49720aab6c3fSTyrel Datwyler 	mad->common.version = cpu_to_be32(1);
49730aab6c3fSTyrel Datwyler 	mad->common.opcode = cpu_to_be32(IBMVFC_DISC_TARGETS);
49740aab6c3fSTyrel Datwyler 	mad->common.length = cpu_to_be16(sizeof(*mad));
49750aab6c3fSTyrel Datwyler 	mad->bufflen = cpu_to_be32(vhost->disc_buf_sz);
49760aab6c3fSTyrel Datwyler 	mad->buffer.va = cpu_to_be64(vhost->disc_buf_dma);
49770aab6c3fSTyrel Datwyler 	mad->buffer.len = cpu_to_be32(vhost->disc_buf_sz);
49784b29cb61SBrian King 	mad->flags = cpu_to_be32(IBMVFC_DISC_TGT_PORT_ID_WWPN_LIST);
4979072b91f9SBrian King 	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
4980072b91f9SBrian King 
4981072b91f9SBrian King 	if (!ibmvfc_send_event(evt, vhost, default_timeout))
4982072b91f9SBrian King 		ibmvfc_dbg(vhost, "Sent discover targets\n");
4983072b91f9SBrian King 	else
4984072b91f9SBrian King 		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
4985072b91f9SBrian King }
4986072b91f9SBrian King 
ibmvfc_channel_setup_done(struct ibmvfc_event * evt)4987e95eef3fSTyrel Datwyler static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
4988e95eef3fSTyrel Datwyler {
4989e95eef3fSTyrel Datwyler 	struct ibmvfc_host *vhost = evt->vhost;
4990b88a5d9bSTyrel Datwyler 	struct ibmvfc_channel_setup *setup = vhost->channel_setup_buf;
4991b88a5d9bSTyrel Datwyler 	struct ibmvfc_scsi_channels *scrqs = &vhost->scsi_scrqs;
4992e95eef3fSTyrel Datwyler 	u32 mad_status = be16_to_cpu(evt->xfer_iu->channel_setup.common.status);
4993e95eef3fSTyrel Datwyler 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
4994b88a5d9bSTyrel Datwyler 	int flags, active_queues, i;
4995e95eef3fSTyrel Datwyler 
4996e95eef3fSTyrel Datwyler 	ibmvfc_free_event(evt);
4997e95eef3fSTyrel Datwyler 
4998e95eef3fSTyrel Datwyler 	switch (mad_status) {
4999e95eef3fSTyrel Datwyler 	case IBMVFC_MAD_SUCCESS:
5000ff79acc4SColin Ian King 		ibmvfc_dbg(vhost, "Channel Setup succeeded\n");
5001b88a5d9bSTyrel Datwyler 		flags = be32_to_cpu(setup->flags);
5002e95eef3fSTyrel Datwyler 		vhost->do_enquiry = 0;
5003b88a5d9bSTyrel Datwyler 		active_queues = be32_to_cpu(setup->num_scsi_subq_channels);
5004b88a5d9bSTyrel Datwyler 		scrqs->active_queues = active_queues;
5005b88a5d9bSTyrel Datwyler 
5006b88a5d9bSTyrel Datwyler 		if (flags & IBMVFC_CHANNELS_CANCELED) {
5007b88a5d9bSTyrel Datwyler 			ibmvfc_dbg(vhost, "Channels Canceled\n");
5008b88a5d9bSTyrel Datwyler 			vhost->using_channels = 0;
5009b88a5d9bSTyrel Datwyler 		} else {
5010b88a5d9bSTyrel Datwyler 			if (active_queues)
5011b88a5d9bSTyrel Datwyler 				vhost->using_channels = 1;
5012b88a5d9bSTyrel Datwyler 			for (i = 0; i < active_queues; i++)
5013b88a5d9bSTyrel Datwyler 				scrqs->scrqs[i].vios_cookie =
5014b88a5d9bSTyrel Datwyler 					be64_to_cpu(setup->channel_handles[i]);
5015b88a5d9bSTyrel Datwyler 
5016b88a5d9bSTyrel Datwyler 			ibmvfc_dbg(vhost, "Using %u channels\n",
5017b88a5d9bSTyrel Datwyler 				   vhost->scsi_scrqs.active_queues);
5018b88a5d9bSTyrel Datwyler 		}
5019e95eef3fSTyrel Datwyler 		break;
5020e95eef3fSTyrel Datwyler 	case IBMVFC_MAD_FAILED:
5021e95eef3fSTyrel Datwyler 		level += ibmvfc_retry_host_init(vhost);
5022e95eef3fSTyrel Datwyler 		ibmvfc_log(vhost, level, "Channel Setup failed\n");
5023e95eef3fSTyrel Datwyler 		fallthrough;
5024e95eef3fSTyrel Datwyler 	case IBMVFC_MAD_DRIVER_FAILED:
5025e95eef3fSTyrel Datwyler 		return;
5026e95eef3fSTyrel Datwyler 	default:
5027e95eef3fSTyrel Datwyler 		dev_err(vhost->dev, "Invalid Channel Setup response: 0x%x\n",
5028e95eef3fSTyrel Datwyler 			mad_status);
5029e95eef3fSTyrel Datwyler 		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
5030e95eef3fSTyrel Datwyler 		return;
5031e95eef3fSTyrel Datwyler 	}
5032e95eef3fSTyrel Datwyler 
5033e95eef3fSTyrel Datwyler 	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
5034e95eef3fSTyrel Datwyler 	wake_up(&vhost->work_wait_q);
5035e95eef3fSTyrel Datwyler }
5036e95eef3fSTyrel Datwyler 
ibmvfc_channel_setup(struct ibmvfc_host * vhost)5037e95eef3fSTyrel Datwyler static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
5038e95eef3fSTyrel Datwyler {
5039e95eef3fSTyrel Datwyler 	struct ibmvfc_channel_setup_mad *mad;
5040e95eef3fSTyrel Datwyler 	struct ibmvfc_channel_setup *setup_buf = vhost->channel_setup_buf;
5041e95eef3fSTyrel Datwyler 	struct ibmvfc_event *evt = ibmvfc_get_event(&vhost->crq);
5042b88a5d9bSTyrel Datwyler 	struct ibmvfc_scsi_channels *scrqs = &vhost->scsi_scrqs;
5043b88a5d9bSTyrel Datwyler 	unsigned int num_channels =
5044b88a5d9bSTyrel Datwyler 		min(vhost->client_scsi_channels, vhost->max_vios_scsi_channels);
5045*8bbe784cSTyrel Datwyler 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
5046b88a5d9bSTyrel Datwyler 	int i;
5047e95eef3fSTyrel Datwyler 
5048*8bbe784cSTyrel Datwyler 	if (!evt) {
5049*8bbe784cSTyrel Datwyler 		ibmvfc_log(vhost, level, "Channel Setup failed: no available events\n");
5050*8bbe784cSTyrel Datwyler 		ibmvfc_hard_reset_host(vhost);
5051*8bbe784cSTyrel Datwyler 		return;
5052*8bbe784cSTyrel Datwyler 	}
5053*8bbe784cSTyrel Datwyler 
5054e95eef3fSTyrel Datwyler 	memset(setup_buf, 0, sizeof(*setup_buf));
5055b88a5d9bSTyrel Datwyler 	if (num_channels == 0)
5056e95eef3fSTyrel Datwyler 		setup_buf->flags = cpu_to_be32(IBMVFC_CANCEL_CHANNELS);
5057b88a5d9bSTyrel Datwyler 	else {
5058b88a5d9bSTyrel Datwyler 		setup_buf->num_scsi_subq_channels = cpu_to_be32(num_channels);
5059b88a5d9bSTyrel Datwyler 		for (i = 0; i < num_channels; i++)
5060b88a5d9bSTyrel Datwyler 			setup_buf->channel_handles[i] = cpu_to_be64(scrqs->scrqs[i].cookie);
5061b88a5d9bSTyrel Datwyler 	}
5062e95eef3fSTyrel Datwyler 
5063e95eef3fSTyrel Datwyler 	ibmvfc_init_event(evt, ibmvfc_channel_setup_done, IBMVFC_MAD_FORMAT);
5064e95eef3fSTyrel Datwyler 	mad = &evt->iu.channel_setup;
5065e95eef3fSTyrel Datwyler 	memset(mad, 0, sizeof(*mad));
5066e95eef3fSTyrel Datwyler 	mad->common.version = cpu_to_be32(1);
5067e95eef3fSTyrel Datwyler 	mad->common.opcode = cpu_to_be32(IBMVFC_CHANNEL_SETUP);
5068e95eef3fSTyrel Datwyler 	mad->common.length = cpu_to_be16(sizeof(*mad));
5069e95eef3fSTyrel Datwyler 	mad->buffer.va = cpu_to_be64(vhost->channel_setup_dma);
5070e95eef3fSTyrel Datwyler 	mad->buffer.len = cpu_to_be32(sizeof(*vhost->channel_setup_buf));
5071e95eef3fSTyrel Datwyler 
5072e95eef3fSTyrel Datwyler 	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
5073e95eef3fSTyrel Datwyler 
5074e95eef3fSTyrel Datwyler 	if (!ibmvfc_send_event(evt, vhost, default_timeout))
5075e95eef3fSTyrel Datwyler 		ibmvfc_dbg(vhost, "Sent channel setup\n");
5076e95eef3fSTyrel Datwyler 	else
5077e95eef3fSTyrel Datwyler 		ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
5078e95eef3fSTyrel Datwyler }
5079e95eef3fSTyrel Datwyler 
ibmvfc_channel_enquiry_done(struct ibmvfc_event * evt)5080e95eef3fSTyrel Datwyler static void ibmvfc_channel_enquiry_done(struct ibmvfc_event *evt)
5081e95eef3fSTyrel Datwyler {
5082e95eef3fSTyrel Datwyler 	struct ibmvfc_host *vhost = evt->vhost;
5083e95eef3fSTyrel Datwyler 	struct ibmvfc_channel_enquiry *rsp = &evt->xfer_iu->channel_enquiry;
5084e95eef3fSTyrel Datwyler 	u32 mad_status = be16_to_cpu(rsp->common.status);
5085e95eef3fSTyrel Datwyler 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
5086e95eef3fSTyrel Datwyler 
5087e95eef3fSTyrel Datwyler 	switch (mad_status) {
5088e95eef3fSTyrel Datwyler 	case IBMVFC_MAD_SUCCESS:
5089e95eef3fSTyrel Datwyler 		ibmvfc_dbg(vhost, "Channel Enquiry succeeded\n");
5090e95eef3fSTyrel Datwyler 		vhost->max_vios_scsi_channels = be32_to_cpu(rsp->num_scsi_subq_channels);
5091e95eef3fSTyrel Datwyler 		ibmvfc_free_event(evt);
5092e95eef3fSTyrel Datwyler 		break;
5093e95eef3fSTyrel Datwyler 	case IBMVFC_MAD_FAILED:
5094e95eef3fSTyrel Datwyler 		level += ibmvfc_retry_host_init(vhost);
5095e95eef3fSTyrel Datwyler 		ibmvfc_log(vhost, level, "Channel Enquiry failed\n");
5096e95eef3fSTyrel Datwyler 		fallthrough;
5097e95eef3fSTyrel Datwyler 	case IBMVFC_MAD_DRIVER_FAILED:
5098e95eef3fSTyrel Datwyler 		ibmvfc_free_event(evt);
5099e95eef3fSTyrel Datwyler 		return;
5100e95eef3fSTyrel Datwyler 	default:
5101e95eef3fSTyrel Datwyler 		dev_err(vhost->dev, "Invalid Channel Enquiry response: 0x%x\n",
5102e95eef3fSTyrel Datwyler 			mad_status);
5103e95eef3fSTyrel Datwyler 		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
5104e95eef3fSTyrel Datwyler 		ibmvfc_free_event(evt);
5105e95eef3fSTyrel Datwyler 		return;
5106e95eef3fSTyrel Datwyler 	}
5107e95eef3fSTyrel Datwyler 
5108e95eef3fSTyrel Datwyler 	ibmvfc_channel_setup(vhost);
5109e95eef3fSTyrel Datwyler }
5110e95eef3fSTyrel Datwyler 
ibmvfc_channel_enquiry(struct ibmvfc_host * vhost)5111e95eef3fSTyrel Datwyler static void ibmvfc_channel_enquiry(struct ibmvfc_host *vhost)
5112e95eef3fSTyrel Datwyler {
5113e95eef3fSTyrel Datwyler 	struct ibmvfc_channel_enquiry *mad;
5114e95eef3fSTyrel Datwyler 	struct ibmvfc_event *evt = ibmvfc_get_event(&vhost->crq);
5115*8bbe784cSTyrel Datwyler 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
5116*8bbe784cSTyrel Datwyler 
5117*8bbe784cSTyrel Datwyler 	if (!evt) {
5118*8bbe784cSTyrel Datwyler 		ibmvfc_log(vhost, level, "Channel Enquiry failed: no available events\n");
5119*8bbe784cSTyrel Datwyler 		ibmvfc_hard_reset_host(vhost);
5120*8bbe784cSTyrel Datwyler 		return;
5121*8bbe784cSTyrel Datwyler 	}
5122e95eef3fSTyrel Datwyler 
5123e95eef3fSTyrel Datwyler 	ibmvfc_init_event(evt, ibmvfc_channel_enquiry_done, IBMVFC_MAD_FORMAT);
5124e95eef3fSTyrel Datwyler 	mad = &evt->iu.channel_enquiry;
5125e95eef3fSTyrel Datwyler 	memset(mad, 0, sizeof(*mad));
5126e95eef3fSTyrel Datwyler 	mad->common.version = cpu_to_be32(1);
5127e95eef3fSTyrel Datwyler 	mad->common.opcode = cpu_to_be32(IBMVFC_CHANNEL_ENQUIRY);
5128e95eef3fSTyrel Datwyler 	mad->common.length = cpu_to_be16(sizeof(*mad));
5129e95eef3fSTyrel Datwyler 
5130032d1900STyrel Datwyler 	if (mig_channels_only)
5131e95eef3fSTyrel Datwyler 		mad->flags |= cpu_to_be32(IBMVFC_NO_CHANNELS_TO_CRQ_SUPPORT);
5132032d1900STyrel Datwyler 	if (mig_no_less_channels)
5133e95eef3fSTyrel Datwyler 		mad->flags |= cpu_to_be32(IBMVFC_NO_N_TO_M_CHANNELS_SUPPORT);
5134e95eef3fSTyrel Datwyler 
5135e95eef3fSTyrel Datwyler 	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
5136e95eef3fSTyrel Datwyler 
5137e95eef3fSTyrel Datwyler 	if (!ibmvfc_send_event(evt, vhost, default_timeout))
5138e95eef3fSTyrel Datwyler 		ibmvfc_dbg(vhost, "Send channel enquiry\n");
5139e95eef3fSTyrel Datwyler 	else
5140e95eef3fSTyrel Datwyler 		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
5141e95eef3fSTyrel Datwyler }
5142e95eef3fSTyrel Datwyler 
5143072b91f9SBrian King /**
5144072b91f9SBrian King  * ibmvfc_npiv_login_done - Completion handler for NPIV Login
5145072b91f9SBrian King  * @evt:	ibmvfc event struct
5146072b91f9SBrian King  *
5147072b91f9SBrian King  **/
ibmvfc_npiv_login_done(struct ibmvfc_event * evt)5148072b91f9SBrian King static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
5149072b91f9SBrian King {
5150072b91f9SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
51510aab6c3fSTyrel Datwyler 	u32 mad_status = be16_to_cpu(evt->xfer_iu->npiv_login.common.status);
5152072b91f9SBrian King 	struct ibmvfc_npiv_login_resp *rsp = &vhost->login_buf->resp;
5153072b91f9SBrian King 	unsigned int npiv_max_sectors;
51547d0e4622SBrian King 	int level = IBMVFC_DEFAULT_LOG_LEVEL;
5155072b91f9SBrian King 
5156072b91f9SBrian King 	switch (mad_status) {
5157072b91f9SBrian King 	case IBMVFC_MAD_SUCCESS:
5158072b91f9SBrian King 		ibmvfc_free_event(evt);
5159072b91f9SBrian King 		break;
5160072b91f9SBrian King 	case IBMVFC_MAD_FAILED:
51610aab6c3fSTyrel Datwyler 		if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
51627d0e4622SBrian King 			level += ibmvfc_retry_host_init(vhost);
5163072b91f9SBrian King 		else
5164072b91f9SBrian King 			ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
51657d0e4622SBrian King 		ibmvfc_log(vhost, level, "NPIV Login failed: %s (%x:%x)\n",
51660aab6c3fSTyrel Datwyler 			   ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
51673e6f7de4STyrel Datwyler 						be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
5168072b91f9SBrian King 		ibmvfc_free_event(evt);
5169072b91f9SBrian King 		return;
5170072b91f9SBrian King 	case IBMVFC_MAD_CRQ_ERROR:
5171072b91f9SBrian King 		ibmvfc_retry_host_init(vhost);
5172df561f66SGustavo A. R. Silva 		fallthrough;
5173072b91f9SBrian King 	case IBMVFC_MAD_DRIVER_FAILED:
5174072b91f9SBrian King 		ibmvfc_free_event(evt);
5175072b91f9SBrian King 		return;
5176072b91f9SBrian King 	default:
5177072b91f9SBrian King 		dev_err(vhost->dev, "Invalid NPIV Login response: 0x%x\n", mad_status);
5178072b91f9SBrian King 		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
5179072b91f9SBrian King 		ibmvfc_free_event(evt);
5180072b91f9SBrian King 		return;
5181072b91f9SBrian King 	}
5182072b91f9SBrian King 
5183072b91f9SBrian King 	vhost->client_migrated = 0;
5184072b91f9SBrian King 
51850aab6c3fSTyrel Datwyler 	if (!(be32_to_cpu(rsp->flags) & IBMVFC_NATIVE_FC)) {
5186072b91f9SBrian King 		dev_err(vhost->dev, "Virtual adapter does not support FC. %x\n",
5187072b91f9SBrian King 			rsp->flags);
5188072b91f9SBrian King 		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
5189072b91f9SBrian King 		wake_up(&vhost->work_wait_q);
5190072b91f9SBrian King 		return;
5191072b91f9SBrian King 	}
5192072b91f9SBrian King 
51930aab6c3fSTyrel Datwyler 	if (be32_to_cpu(rsp->max_cmds) <= IBMVFC_NUM_INTERNAL_REQ) {
5194072b91f9SBrian King 		dev_err(vhost->dev, "Virtual adapter supported queue depth too small: %d\n",
5195072b91f9SBrian King 			rsp->max_cmds);
5196072b91f9SBrian King 		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
5197072b91f9SBrian King 		wake_up(&vhost->work_wait_q);
5198072b91f9SBrian King 		return;
5199072b91f9SBrian King 	}
5200072b91f9SBrian King 
520179111d08SBrian King 	vhost->logged_in = 1;
52020aab6c3fSTyrel Datwyler 	npiv_max_sectors = min((uint)(be64_to_cpu(rsp->max_dma_len) >> 9), IBMVFC_MAX_SECTORS);
5203072b91f9SBrian King 	dev_info(vhost->dev, "Host partition: %s, device: %s %s %s max sectors %u\n",
5204072b91f9SBrian King 		 rsp->partition_name, rsp->device_name, rsp->port_loc_code,
5205072b91f9SBrian King 		 rsp->drc_name, npiv_max_sectors);
5206072b91f9SBrian King 
52070aab6c3fSTyrel Datwyler 	fc_host_fabric_name(vhost->host) = be64_to_cpu(rsp->node_name);
52080aab6c3fSTyrel Datwyler 	fc_host_node_name(vhost->host) = be64_to_cpu(rsp->node_name);
52090aab6c3fSTyrel Datwyler 	fc_host_port_name(vhost->host) = be64_to_cpu(rsp->port_name);
52100aab6c3fSTyrel Datwyler 	fc_host_port_id(vhost->host) = be64_to_cpu(rsp->scsi_id);
5211072b91f9SBrian King 	fc_host_port_type(vhost->host) = FC_PORTTYPE_NPIV;
5212072b91f9SBrian King 	fc_host_supported_classes(vhost->host) = 0;
52130aab6c3fSTyrel Datwyler 	if (be32_to_cpu(rsp->service_parms.class1_parms[0]) & 0x80000000)
5214072b91f9SBrian King 		fc_host_supported_classes(vhost->host) |= FC_COS_CLASS1;
52150aab6c3fSTyrel Datwyler 	if (be32_to_cpu(rsp->service_parms.class2_parms[0]) & 0x80000000)
5216072b91f9SBrian King 		fc_host_supported_classes(vhost->host) |= FC_COS_CLASS2;
52170aab6c3fSTyrel Datwyler 	if (be32_to_cpu(rsp->service_parms.class3_parms[0]) & 0x80000000)
5218072b91f9SBrian King 		fc_host_supported_classes(vhost->host) |= FC_COS_CLASS3;
5219072b91f9SBrian King 	fc_host_maxframe_size(vhost->host) =
52200aab6c3fSTyrel Datwyler 		be16_to_cpu(rsp->service_parms.common.bb_rcv_sz) & 0x0fff;
5221072b91f9SBrian King 
52220aab6c3fSTyrel Datwyler 	vhost->host->can_queue = be32_to_cpu(rsp->max_cmds) - IBMVFC_NUM_INTERNAL_REQ;
5223072b91f9SBrian King 	vhost->host->max_sectors = npiv_max_sectors;
5224e95eef3fSTyrel Datwyler 
5225e95eef3fSTyrel Datwyler 	if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS) && vhost->do_enquiry) {
5226e95eef3fSTyrel Datwyler 		ibmvfc_channel_enquiry(vhost);
5227e95eef3fSTyrel Datwyler 	} else {
5228e95eef3fSTyrel Datwyler 		vhost->do_enquiry = 0;
5229072b91f9SBrian King 		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
5230072b91f9SBrian King 		wake_up(&vhost->work_wait_q);
5231072b91f9SBrian King 	}
5232e95eef3fSTyrel Datwyler }
5233072b91f9SBrian King 
5234072b91f9SBrian King /**
5235072b91f9SBrian King  * ibmvfc_npiv_login - Sends NPIV login
5236072b91f9SBrian King  * @vhost:	ibmvfc host struct
5237072b91f9SBrian King  *
5238072b91f9SBrian King  **/
ibmvfc_npiv_login(struct ibmvfc_host * vhost)5239072b91f9SBrian King static void ibmvfc_npiv_login(struct ibmvfc_host *vhost)
5240072b91f9SBrian King {
5241072b91f9SBrian King 	struct ibmvfc_npiv_login_mad *mad;
5242e4b26f3dSTyrel Datwyler 	struct ibmvfc_event *evt = ibmvfc_get_event(&vhost->crq);
5243072b91f9SBrian King 
5244*8bbe784cSTyrel Datwyler 	if (!evt) {
5245*8bbe784cSTyrel Datwyler 		ibmvfc_dbg(vhost, "NPIV Login failed: no available events\n");
5246*8bbe784cSTyrel Datwyler 		ibmvfc_hard_reset_host(vhost);
5247*8bbe784cSTyrel Datwyler 		return;
5248*8bbe784cSTyrel Datwyler 	}
5249*8bbe784cSTyrel Datwyler 
5250072b91f9SBrian King 	ibmvfc_gather_partition_info(vhost);
5251072b91f9SBrian King 	ibmvfc_set_login_info(vhost);
5252072b91f9SBrian King 	ibmvfc_init_event(evt, ibmvfc_npiv_login_done, IBMVFC_MAD_FORMAT);
5253072b91f9SBrian King 
5254072b91f9SBrian King 	memcpy(vhost->login_buf, &vhost->login_info, sizeof(vhost->login_info));
5255072b91f9SBrian King 	mad = &evt->iu.npiv_login;
5256072b91f9SBrian King 	memset(mad, 0, sizeof(struct ibmvfc_npiv_login_mad));
52570aab6c3fSTyrel Datwyler 	mad->common.version = cpu_to_be32(1);
52580aab6c3fSTyrel Datwyler 	mad->common.opcode = cpu_to_be32(IBMVFC_NPIV_LOGIN);
52590aab6c3fSTyrel Datwyler 	mad->common.length = cpu_to_be16(sizeof(struct ibmvfc_npiv_login_mad));
52600aab6c3fSTyrel Datwyler 	mad->buffer.va = cpu_to_be64(vhost->login_buf_dma);
52610aab6c3fSTyrel Datwyler 	mad->buffer.len = cpu_to_be32(sizeof(*vhost->login_buf));
5262072b91f9SBrian King 
5263072b91f9SBrian King 	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
5264072b91f9SBrian King 
5265072b91f9SBrian King 	if (!ibmvfc_send_event(evt, vhost, default_timeout))
5266072b91f9SBrian King 		ibmvfc_dbg(vhost, "Sent NPIV login\n");
5267072b91f9SBrian King 	else
5268072b91f9SBrian King 		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
52694e071619STyrel Datwyler }
5270072b91f9SBrian King 
5271072b91f9SBrian King /**
527279111d08SBrian King  * ibmvfc_npiv_logout_done - Completion handler for NPIV Logout
5273dd9c7729SLee Jones  * @evt:		ibmvfc event struct
527479111d08SBrian King  *
527579111d08SBrian King  **/
ibmvfc_npiv_logout_done(struct ibmvfc_event * evt)527679111d08SBrian King static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt)
527779111d08SBrian King {
527879111d08SBrian King 	struct ibmvfc_host *vhost = evt->vhost;
52790aab6c3fSTyrel Datwyler 	u32 mad_status = be16_to_cpu(evt->xfer_iu->npiv_logout.common.status);
528079111d08SBrian King 
528179111d08SBrian King 	ibmvfc_free_event(evt);
528279111d08SBrian King 
528379111d08SBrian King 	switch (mad_status) {
528479111d08SBrian King 	case IBMVFC_MAD_SUCCESS:
5285e4b26f3dSTyrel Datwyler 		if (list_empty(&vhost->crq.sent) &&
528679111d08SBrian King 		    vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) {
5287861890c6SBrian King 			ibmvfc_init_host(vhost);
528879111d08SBrian King 			return;
528979111d08SBrian King 		}
529079111d08SBrian King 		break;
529179111d08SBrian King 	case IBMVFC_MAD_FAILED:
529279111d08SBrian King 	case IBMVFC_MAD_NOT_SUPPORTED:
529379111d08SBrian King 	case IBMVFC_MAD_CRQ_ERROR:
529479111d08SBrian King 	case IBMVFC_MAD_DRIVER_FAILED:
529579111d08SBrian King 	default:
529679111d08SBrian King 		ibmvfc_dbg(vhost, "NPIV Logout failed. 0x%X\n", mad_status);
529779111d08SBrian King 		break;
529879111d08SBrian King 	}
529979111d08SBrian King 
530079111d08SBrian King 	ibmvfc_hard_reset_host(vhost);
530179111d08SBrian King }
530279111d08SBrian King 
530379111d08SBrian King /**
530479111d08SBrian King  * ibmvfc_npiv_logout - Issue an NPIV Logout
530579111d08SBrian King  * @vhost:		ibmvfc host struct
530679111d08SBrian King  *
530779111d08SBrian King  **/
ibmvfc_npiv_logout(struct ibmvfc_host * vhost)530879111d08SBrian King static void ibmvfc_npiv_logout(struct ibmvfc_host *vhost)
530979111d08SBrian King {
531079111d08SBrian King 	struct ibmvfc_npiv_logout_mad *mad;
531179111d08SBrian King 	struct ibmvfc_event *evt;
531279111d08SBrian King 
5313e4b26f3dSTyrel Datwyler 	evt = ibmvfc_get_event(&vhost->crq);
5314*8bbe784cSTyrel Datwyler 	if (!evt) {
5315*8bbe784cSTyrel Datwyler 		ibmvfc_dbg(vhost, "NPIV Logout failed: no available events\n");
5316*8bbe784cSTyrel Datwyler 		ibmvfc_hard_reset_host(vhost);
5317*8bbe784cSTyrel Datwyler 		return;
5318*8bbe784cSTyrel Datwyler 	}
5319*8bbe784cSTyrel Datwyler 
532079111d08SBrian King 	ibmvfc_init_event(evt, ibmvfc_npiv_logout_done, IBMVFC_MAD_FORMAT);
532179111d08SBrian King 
532279111d08SBrian King 	mad = &evt->iu.npiv_logout;
532379111d08SBrian King 	memset(mad, 0, sizeof(*mad));
53240aab6c3fSTyrel Datwyler 	mad->common.version = cpu_to_be32(1);
53250aab6c3fSTyrel Datwyler 	mad->common.opcode = cpu_to_be32(IBMVFC_NPIV_LOGOUT);
53260aab6c3fSTyrel Datwyler 	mad->common.length = cpu_to_be16(sizeof(struct ibmvfc_npiv_logout_mad));
532779111d08SBrian King 
532879111d08SBrian King 	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_LOGO_WAIT);
532979111d08SBrian King 
533079111d08SBrian King 	if (!ibmvfc_send_event(evt, vhost, default_timeout))
533179111d08SBrian King 		ibmvfc_dbg(vhost, "Sent NPIV logout\n");
533279111d08SBrian King 	else
533379111d08SBrian King 		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
533479111d08SBrian King }
533579111d08SBrian King 
533679111d08SBrian King /**
5337072b91f9SBrian King  * ibmvfc_dev_init_to_do - Is there target initialization work to do?
5338072b91f9SBrian King  * @vhost:		ibmvfc host struct
5339072b91f9SBrian King  *
5340072b91f9SBrian King  * Returns:
5341072b91f9SBrian King  *	1 if work to do / 0 if not
5342072b91f9SBrian King  **/
ibmvfc_dev_init_to_do(struct ibmvfc_host * vhost)5343072b91f9SBrian King static int ibmvfc_dev_init_to_do(struct ibmvfc_host *vhost)
5344072b91f9SBrian King {
5345072b91f9SBrian King 	struct ibmvfc_target *tgt;
5346072b91f9SBrian King 
5347072b91f9SBrian King 	list_for_each_entry(tgt, &vhost->targets, queue) {
5348072b91f9SBrian King 		if (tgt->action == IBMVFC_TGT_ACTION_INIT ||
5349072b91f9SBrian King 		    tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
5350072b91f9SBrian King 			return 1;
5351072b91f9SBrian King 	}
5352072b91f9SBrian King 
5353072b91f9SBrian King 	return 0;
5354072b91f9SBrian King }
5355072b91f9SBrian King 
5356072b91f9SBrian King /**
5357ed830385SBrian King  * ibmvfc_dev_logo_to_do - Is there target logout work to do?
5358ed830385SBrian King  * @vhost:		ibmvfc host struct
5359ed830385SBrian King  *
5360ed830385SBrian King  * Returns:
5361ed830385SBrian King  *	1 if work to do / 0 if not
5362ed830385SBrian King  **/
ibmvfc_dev_logo_to_do(struct ibmvfc_host * vhost)5363ed830385SBrian King static int ibmvfc_dev_logo_to_do(struct ibmvfc_host *vhost)
5364ed830385SBrian King {
5365ed830385SBrian King 	struct ibmvfc_target *tgt;
5366ed830385SBrian King 
5367ed830385SBrian King 	list_for_each_entry(tgt, &vhost->targets, queue) {
5368ed830385SBrian King 		if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT ||
5369ed830385SBrian King 		    tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
5370ed830385SBrian King 			return 1;
5371ed830385SBrian King 	}
5372ed830385SBrian King 	return 0;
5373ed830385SBrian King }
5374ed830385SBrian King 
5375ed830385SBrian King /**
5376072b91f9SBrian King  * __ibmvfc_work_to_do - Is there task level work to do? (no locking)
5377072b91f9SBrian King  * @vhost:		ibmvfc host struct
5378072b91f9SBrian King  *
5379072b91f9SBrian King  * Returns:
5380072b91f9SBrian King  *	1 if work to do / 0 if not
5381072b91f9SBrian King  **/
__ibmvfc_work_to_do(struct ibmvfc_host * vhost)5382072b91f9SBrian King static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
5383072b91f9SBrian King {
5384072b91f9SBrian King 	struct ibmvfc_target *tgt;
5385072b91f9SBrian King 
5386072b91f9SBrian King 	if (kthread_should_stop())
5387072b91f9SBrian King 		return 1;
5388072b91f9SBrian King 	switch (vhost->action) {
5389072b91f9SBrian King 	case IBMVFC_HOST_ACTION_NONE:
5390072b91f9SBrian King 	case IBMVFC_HOST_ACTION_INIT_WAIT:
539179111d08SBrian King 	case IBMVFC_HOST_ACTION_LOGO_WAIT:
5392072b91f9SBrian King 		return 0;
5393072b91f9SBrian King 	case IBMVFC_HOST_ACTION_TGT_INIT:
5394072b91f9SBrian King 	case IBMVFC_HOST_ACTION_QUERY_TGTS:
5395072b91f9SBrian King 		if (vhost->discovery_threads == disc_threads)
5396072b91f9SBrian King 			return 0;
5397072b91f9SBrian King 		list_for_each_entry(tgt, &vhost->targets, queue)
5398072b91f9SBrian King 			if (tgt->action == IBMVFC_TGT_ACTION_INIT)
5399072b91f9SBrian King 				return 1;
5400072b91f9SBrian King 		list_for_each_entry(tgt, &vhost->targets, queue)
5401072b91f9SBrian King 			if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
5402072b91f9SBrian King 				return 0;
5403072b91f9SBrian King 		return 1;
5404ed830385SBrian King 	case IBMVFC_HOST_ACTION_TGT_DEL:
5405ed830385SBrian King 	case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
5406ed830385SBrian King 		if (vhost->discovery_threads == disc_threads)
5407ed830385SBrian King 			return 0;
5408ed830385SBrian King 		list_for_each_entry(tgt, &vhost->targets, queue)
5409ed830385SBrian King 			if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT)
5410ed830385SBrian King 				return 1;
5411ed830385SBrian King 		list_for_each_entry(tgt, &vhost->targets, queue)
5412ed830385SBrian King 			if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
5413ed830385SBrian King 				return 0;
5414ed830385SBrian King 		return 1;
541579111d08SBrian King 	case IBMVFC_HOST_ACTION_LOGO:
5416072b91f9SBrian King 	case IBMVFC_HOST_ACTION_INIT:
5417072b91f9SBrian King 	case IBMVFC_HOST_ACTION_ALLOC_TGTS:
5418072b91f9SBrian King 	case IBMVFC_HOST_ACTION_QUERY:
541973ee5d86SBrian King 	case IBMVFC_HOST_ACTION_RESET:
542073ee5d86SBrian King 	case IBMVFC_HOST_ACTION_REENABLE:
5421072b91f9SBrian King 	default:
5422072b91f9SBrian King 		break;
5423f36cfe6aSChristopher Díaz Riveros 	}
5424072b91f9SBrian King 
5425072b91f9SBrian King 	return 1;
5426072b91f9SBrian King }
5427072b91f9SBrian King 
5428072b91f9SBrian King /**
5429072b91f9SBrian King  * ibmvfc_work_to_do - Is there task level work to do?
5430072b91f9SBrian King  * @vhost:		ibmvfc host struct
5431072b91f9SBrian King  *
5432072b91f9SBrian King  * Returns:
5433072b91f9SBrian King  *	1 if work to do / 0 if not
5434072b91f9SBrian King  **/
ibmvfc_work_to_do(struct ibmvfc_host * vhost)5435072b91f9SBrian King static int ibmvfc_work_to_do(struct ibmvfc_host *vhost)
5436072b91f9SBrian King {
5437072b91f9SBrian King 	unsigned long flags;
5438072b91f9SBrian King 	int rc;
5439072b91f9SBrian King 
5440072b91f9SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
5441072b91f9SBrian King 	rc = __ibmvfc_work_to_do(vhost);
5442072b91f9SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
5443072b91f9SBrian King 	return rc;
5444072b91f9SBrian King }
5445072b91f9SBrian King 
5446072b91f9SBrian King /**
5447072b91f9SBrian King  * ibmvfc_log_ae - Log async events if necessary
5448072b91f9SBrian King  * @vhost:		ibmvfc host struct
5449072b91f9SBrian King  * @events:		events to log
5450072b91f9SBrian King  *
5451072b91f9SBrian King  **/
ibmvfc_log_ae(struct ibmvfc_host * vhost,int events)5452072b91f9SBrian King static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events)
5453072b91f9SBrian King {
5454072b91f9SBrian King 	if (events & IBMVFC_AE_RSCN)
5455072b91f9SBrian King 		fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_RSCN, 0);
5456072b91f9SBrian King 	if ((events & IBMVFC_AE_LINKDOWN) &&
5457072b91f9SBrian King 	    vhost->state >= IBMVFC_HALTED)
5458072b91f9SBrian King 		fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_LINKDOWN, 0);
5459072b91f9SBrian King 	if ((events & IBMVFC_AE_LINKUP) &&
5460072b91f9SBrian King 	    vhost->state == IBMVFC_INITIALIZING)
5461072b91f9SBrian King 		fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_LINKUP, 0);
5462072b91f9SBrian King }
5463072b91f9SBrian King 
5464072b91f9SBrian King /**
5465072b91f9SBrian King  * ibmvfc_tgt_add_rport - Tell the FC transport about a new remote port
5466072b91f9SBrian King  * @tgt:		ibmvfc target struct
5467072b91f9SBrian King  *
5468072b91f9SBrian King  **/
ibmvfc_tgt_add_rport(struct ibmvfc_target * tgt)5469072b91f9SBrian King static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
5470072b91f9SBrian King {
5471072b91f9SBrian King 	struct ibmvfc_host *vhost = tgt->vhost;
547243c8da90SBrian King 	struct fc_rport *rport;
5473072b91f9SBrian King 	unsigned long flags;
5474072b91f9SBrian King 
5475072b91f9SBrian King 	tgt_dbg(tgt, "Adding rport\n");
5476072b91f9SBrian King 	rport = fc_remote_port_add(vhost->host, 0, &tgt->ids);
5477072b91f9SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
547843c8da90SBrian King 
547943c8da90SBrian King 	if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
548043c8da90SBrian King 		tgt_dbg(tgt, "Deleting rport\n");
548143c8da90SBrian King 		list_del(&tgt->queue);
5482d5da3040SBrian King 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
548343c8da90SBrian King 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
548443c8da90SBrian King 		fc_remote_port_delete(rport);
548543c8da90SBrian King 		del_timer_sync(&tgt->timer);
548643c8da90SBrian King 		kref_put(&tgt->kref, ibmvfc_release_tgt);
548743c8da90SBrian King 		return;
54884b29cb61SBrian King 	} else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
54894b29cb61SBrian King 		tgt_dbg(tgt, "Deleting rport with outstanding I/O\n");
54904b29cb61SBrian King 		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
54914b29cb61SBrian King 		tgt->rport = NULL;
54922e51f78bSBrian King 		tgt->init_retries = 0;
54934b29cb61SBrian King 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
54944b29cb61SBrian King 		fc_remote_port_delete(rport);
54954b29cb61SBrian King 		return;
5496d5da3040SBrian King 	} else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
5497d5da3040SBrian King 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
5498d5da3040SBrian King 		return;
549943c8da90SBrian King 	}
550043c8da90SBrian King 
5501072b91f9SBrian King 	if (rport) {
5502072b91f9SBrian King 		tgt_dbg(tgt, "rport add succeeded\n");
550343c8da90SBrian King 		tgt->rport = rport;
55040aab6c3fSTyrel Datwyler 		rport->maxframe_size = be16_to_cpu(tgt->service_parms.common.bb_rcv_sz) & 0x0fff;
5505072b91f9SBrian King 		rport->supported_classes = 0;
550652d7e861SBrian King 		tgt->target_id = rport->scsi_target_id;
55070aab6c3fSTyrel Datwyler 		if (be32_to_cpu(tgt->service_parms.class1_parms[0]) & 0x80000000)
5508072b91f9SBrian King 			rport->supported_classes |= FC_COS_CLASS1;
55090aab6c3fSTyrel Datwyler 		if (be32_to_cpu(tgt->service_parms.class2_parms[0]) & 0x80000000)
5510072b91f9SBrian King 			rport->supported_classes |= FC_COS_CLASS2;
55110aab6c3fSTyrel Datwyler 		if (be32_to_cpu(tgt->service_parms.class3_parms[0]) & 0x80000000)
5512072b91f9SBrian King 			rport->supported_classes |= FC_COS_CLASS3;
5513d31429e1SBrian King 		if (rport->rqst_q)
55148a78362cSMartin K. Petersen 			blk_queue_max_segments(rport->rqst_q, 1);
5515072b91f9SBrian King 	} else
5516072b91f9SBrian King 		tgt_dbg(tgt, "rport add failed\n");
5517072b91f9SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
5518072b91f9SBrian King }
5519072b91f9SBrian King 
5520072b91f9SBrian King /**
5521072b91f9SBrian King  * ibmvfc_do_work - Do task level work
5522072b91f9SBrian King  * @vhost:		ibmvfc host struct
5523072b91f9SBrian King  *
5524072b91f9SBrian King  **/
ibmvfc_do_work(struct ibmvfc_host * vhost)5525072b91f9SBrian King static void ibmvfc_do_work(struct ibmvfc_host *vhost)
5526072b91f9SBrian King {
5527072b91f9SBrian King 	struct ibmvfc_target *tgt;
5528072b91f9SBrian King 	unsigned long flags;
5529072b91f9SBrian King 	struct fc_rport *rport;
553057e80e0bSTyrel Datwyler 	LIST_HEAD(purge);
553173ee5d86SBrian King 	int rc;
5532072b91f9SBrian King 
5533072b91f9SBrian King 	ibmvfc_log_ae(vhost, vhost->events_to_log);
5534072b91f9SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
5535072b91f9SBrian King 	vhost->events_to_log = 0;
5536072b91f9SBrian King 	switch (vhost->action) {
5537072b91f9SBrian King 	case IBMVFC_HOST_ACTION_NONE:
553879111d08SBrian King 	case IBMVFC_HOST_ACTION_LOGO_WAIT:
5539072b91f9SBrian King 	case IBMVFC_HOST_ACTION_INIT_WAIT:
5540072b91f9SBrian King 		break;
554173ee5d86SBrian King 	case IBMVFC_HOST_ACTION_RESET:
554257e80e0bSTyrel Datwyler 		list_splice_init(&vhost->purge, &purge);
554373ee5d86SBrian King 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
55441f4a4a19STyrel Datwyler 		ibmvfc_complete_purge(&purge);
554573ee5d86SBrian King 		rc = ibmvfc_reset_crq(vhost);
554615cfef86SBrian King 
554773ee5d86SBrian King 		spin_lock_irqsave(vhost->host->host_lock, flags);
554815cfef86SBrian King 		if (!rc || rc == H_CLOSED)
5549d24099dfSBrian King 			vio_enable_interrupts(to_vio_dev(vhost->dev));
555015cfef86SBrian King 		if (vhost->action == IBMVFC_HOST_ACTION_RESET) {
555115cfef86SBrian King 			/*
555215cfef86SBrian King 			 * The only action we could have changed to would have
555315cfef86SBrian King 			 * been reenable, in which case, we skip the rest of
555415cfef86SBrian King 			 * this path and wait until we've done the re-enable
555515cfef86SBrian King 			 * before sending the crq init.
555615cfef86SBrian King 			 */
555715cfef86SBrian King 			vhost->action = IBMVFC_HOST_ACTION_TGT_DEL;
555815cfef86SBrian King 
555938553564SBrian King 			if (rc || (rc = ibmvfc_send_crq_init(vhost)) ||
556073ee5d86SBrian King 			    (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) {
556173ee5d86SBrian King 				ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
556273ee5d86SBrian King 				dev_err(vhost->dev, "Error after reset (rc=%d)\n", rc);
556373ee5d86SBrian King 			}
556415cfef86SBrian King 		}
556573ee5d86SBrian King 		break;
556673ee5d86SBrian King 	case IBMVFC_HOST_ACTION_REENABLE:
556757e80e0bSTyrel Datwyler 		list_splice_init(&vhost->purge, &purge);
556873ee5d86SBrian King 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
55691f4a4a19STyrel Datwyler 		ibmvfc_complete_purge(&purge);
557073ee5d86SBrian King 		rc = ibmvfc_reenable_crq_queue(vhost);
557115cfef86SBrian King 
557273ee5d86SBrian King 		spin_lock_irqsave(vhost->host->host_lock, flags);
557315cfef86SBrian King 		if (vhost->action == IBMVFC_HOST_ACTION_REENABLE) {
557415cfef86SBrian King 			/*
557515cfef86SBrian King 			 * The only action we could have changed to would have
557615cfef86SBrian King 			 * been reset, in which case, we skip the rest of this
557715cfef86SBrian King 			 * path and wait until we've done the reset before
557815cfef86SBrian King 			 * sending the crq init.
557915cfef86SBrian King 			 */
558015cfef86SBrian King 			vhost->action = IBMVFC_HOST_ACTION_TGT_DEL;
558173ee5d86SBrian King 			if (rc || (rc = ibmvfc_send_crq_init(vhost))) {
558273ee5d86SBrian King 				ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
558373ee5d86SBrian King 				dev_err(vhost->dev, "Error after enable (rc=%d)\n", rc);
558473ee5d86SBrian King 			}
558515cfef86SBrian King 		}
558673ee5d86SBrian King 		break;
558779111d08SBrian King 	case IBMVFC_HOST_ACTION_LOGO:
558879111d08SBrian King 		vhost->job_step(vhost);
558979111d08SBrian King 		break;
5590072b91f9SBrian King 	case IBMVFC_HOST_ACTION_INIT:
5591072b91f9SBrian King 		BUG_ON(vhost->state != IBMVFC_INITIALIZING);
55921c41fa82SBrian King 		if (vhost->delay_init) {
55931c41fa82SBrian King 			vhost->delay_init = 0;
55941c41fa82SBrian King 			spin_unlock_irqrestore(vhost->host->host_lock, flags);
5595d2131b33SBrian King 			ssleep(15);
55961c41fa82SBrian King 			return;
55971c41fa82SBrian King 		} else
5598072b91f9SBrian King 			vhost->job_step(vhost);
5599072b91f9SBrian King 		break;
5600072b91f9SBrian King 	case IBMVFC_HOST_ACTION_QUERY:
5601072b91f9SBrian King 		list_for_each_entry(tgt, &vhost->targets, queue)
5602072b91f9SBrian King 			ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target);
5603072b91f9SBrian King 		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY_TGTS);
5604072b91f9SBrian King 		break;
5605072b91f9SBrian King 	case IBMVFC_HOST_ACTION_QUERY_TGTS:
5606072b91f9SBrian King 		list_for_each_entry(tgt, &vhost->targets, queue) {
5607072b91f9SBrian King 			if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
5608072b91f9SBrian King 				tgt->job_step(tgt);
5609072b91f9SBrian King 				break;
5610072b91f9SBrian King 			}
5611072b91f9SBrian King 		}
5612072b91f9SBrian King 
5613072b91f9SBrian King 		if (!ibmvfc_dev_init_to_do(vhost))
5614072b91f9SBrian King 			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
5615072b91f9SBrian King 		break;
5616072b91f9SBrian King 	case IBMVFC_HOST_ACTION_TGT_DEL:
561710e79499SBrian King 	case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
5618072b91f9SBrian King 		list_for_each_entry(tgt, &vhost->targets, queue) {
5619ed830385SBrian King 			if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
5620ed830385SBrian King 				tgt->job_step(tgt);
5621ed830385SBrian King 				break;
5622ed830385SBrian King 			}
5623ed830385SBrian King 		}
5624ed830385SBrian King 
5625ed830385SBrian King 		if (ibmvfc_dev_logo_to_do(vhost)) {
5626ed830385SBrian King 			spin_unlock_irqrestore(vhost->host->host_lock, flags);
5627ed830385SBrian King 			return;
5628ed830385SBrian King 		}
5629ed830385SBrian King 
5630ed830385SBrian King 		list_for_each_entry(tgt, &vhost->targets, queue) {
5631072b91f9SBrian King 			if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
5632072b91f9SBrian King 				tgt_dbg(tgt, "Deleting rport\n");
5633072b91f9SBrian King 				rport = tgt->rport;
5634072b91f9SBrian King 				tgt->rport = NULL;
5635072b91f9SBrian King 				list_del(&tgt->queue);
5636d5da3040SBrian King 				ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
5637072b91f9SBrian King 				spin_unlock_irqrestore(vhost->host->host_lock, flags);
5638072b91f9SBrian King 				if (rport)
5639072b91f9SBrian King 					fc_remote_port_delete(rport);
564010501e1cSBrian King 				del_timer_sync(&tgt->timer);
5641072b91f9SBrian King 				kref_put(&tgt->kref, ibmvfc_release_tgt);
5642072b91f9SBrian King 				return;
56434b29cb61SBrian King 			} else if (tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
56444b29cb61SBrian King 				tgt_dbg(tgt, "Deleting rport with I/O outstanding\n");
56454b29cb61SBrian King 				rport = tgt->rport;
56464b29cb61SBrian King 				tgt->rport = NULL;
56472e51f78bSBrian King 				tgt->init_retries = 0;
56484b29cb61SBrian King 				ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
56495114975eSBrian King 
56505114975eSBrian King 				/*
56515114975eSBrian King 				 * If fast fail is enabled, we wait for it to fire and then clean up
56525114975eSBrian King 				 * the old port, since we expect the fast fail timer to clean up the
56535114975eSBrian King 				 * outstanding I/O faster than waiting for normal command timeouts.
56545114975eSBrian King 				 * However, if fast fail is disabled, any I/O outstanding to the
56555114975eSBrian King 				 * rport LUNs will stay outstanding indefinitely, since the EH handlers
56565114975eSBrian King 				 * won't get invoked for I/O's timing out. If this is a NPIV failover
56575114975eSBrian King 				 * scenario, the better alternative is to use the move login.
56585114975eSBrian King 				 */
56595114975eSBrian King 				if (rport && rport->fast_io_fail_tmo == -1)
56605114975eSBrian King 					tgt->move_login = 1;
56614b29cb61SBrian King 				spin_unlock_irqrestore(vhost->host->host_lock, flags);
56624b29cb61SBrian King 				if (rport)
56634b29cb61SBrian King 					fc_remote_port_delete(rport);
56644b29cb61SBrian King 				return;
5665072b91f9SBrian King 			}
5666072b91f9SBrian King 		}
5667072b91f9SBrian King 
5668072b91f9SBrian King 		if (vhost->state == IBMVFC_INITIALIZING) {
566910e79499SBrian King 			if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) {
567043c8da90SBrian King 				if (vhost->reinit) {
567143c8da90SBrian King 					vhost->reinit = 0;
567243c8da90SBrian King 					scsi_block_requests(vhost->host);
567343c8da90SBrian King 					ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
567443c8da90SBrian King 					spin_unlock_irqrestore(vhost->host->host_lock, flags);
567543c8da90SBrian King 				} else {
567610e79499SBrian King 					ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
567743c8da90SBrian King 					ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
567843c8da90SBrian King 					wake_up(&vhost->init_wait_q);
567943c8da90SBrian King 					schedule_work(&vhost->rport_add_work_q);
568010e79499SBrian King 					vhost->init_retries = 0;
568110e79499SBrian King 					spin_unlock_irqrestore(vhost->host->host_lock, flags);
568210e79499SBrian King 					scsi_unblock_requests(vhost->host);
568343c8da90SBrian King 				}
568443c8da90SBrian King 
568510e79499SBrian King 				return;
568610e79499SBrian King 			} else {
5687072b91f9SBrian King 				ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
5688072b91f9SBrian King 				vhost->job_step = ibmvfc_discover_targets;
568910e79499SBrian King 			}
5690072b91f9SBrian King 		} else {
5691072b91f9SBrian King 			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
5692072b91f9SBrian King 			spin_unlock_irqrestore(vhost->host->host_lock, flags);
5693072b91f9SBrian King 			scsi_unblock_requests(vhost->host);
5694072b91f9SBrian King 			wake_up(&vhost->init_wait_q);
5695072b91f9SBrian King 			return;
5696072b91f9SBrian King 		}
5697072b91f9SBrian King 		break;
5698072b91f9SBrian King 	case IBMVFC_HOST_ACTION_ALLOC_TGTS:
5699072b91f9SBrian King 		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_INIT);
5700072b91f9SBrian King 		spin_unlock_irqrestore(vhost->host->host_lock, flags);
5701072b91f9SBrian King 		ibmvfc_alloc_targets(vhost);
5702072b91f9SBrian King 		spin_lock_irqsave(vhost->host->host_lock, flags);
5703072b91f9SBrian King 		break;
5704072b91f9SBrian King 	case IBMVFC_HOST_ACTION_TGT_INIT:
5705072b91f9SBrian King 		list_for_each_entry(tgt, &vhost->targets, queue) {
5706072b91f9SBrian King 			if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
5707072b91f9SBrian King 				tgt->job_step(tgt);
5708072b91f9SBrian King 				break;
5709072b91f9SBrian King 			}
5710072b91f9SBrian King 		}
5711072b91f9SBrian King 
571210e79499SBrian King 		if (!ibmvfc_dev_init_to_do(vhost))
571310e79499SBrian King 			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL_FAILED);
5714072b91f9SBrian King 		break;
5715072b91f9SBrian King 	default:
5716072b91f9SBrian King 		break;
5717f36cfe6aSChristopher Díaz Riveros 	}
5718072b91f9SBrian King 
5719072b91f9SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
5720072b91f9SBrian King }
5721072b91f9SBrian King 
5722072b91f9SBrian King /**
5723072b91f9SBrian King  * ibmvfc_work - Do task level work
5724072b91f9SBrian King  * @data:		ibmvfc host struct
5725072b91f9SBrian King  *
5726072b91f9SBrian King  * Returns:
5727072b91f9SBrian King  *	zero
5728072b91f9SBrian King  **/
ibmvfc_work(void * data)5729072b91f9SBrian King static int ibmvfc_work(void *data)
5730072b91f9SBrian King {
5731072b91f9SBrian King 	struct ibmvfc_host *vhost = data;
5732072b91f9SBrian King 	int rc;
5733072b91f9SBrian King 
57348698a745SDongsheng Yang 	set_user_nice(current, MIN_NICE);
5735072b91f9SBrian King 
5736072b91f9SBrian King 	while (1) {
5737072b91f9SBrian King 		rc = wait_event_interruptible(vhost->work_wait_q,
5738072b91f9SBrian King 					      ibmvfc_work_to_do(vhost));
5739072b91f9SBrian King 
5740072b91f9SBrian King 		BUG_ON(rc);
5741072b91f9SBrian King 
5742072b91f9SBrian King 		if (kthread_should_stop())
5743072b91f9SBrian King 			break;
5744072b91f9SBrian King 
5745072b91f9SBrian King 		ibmvfc_do_work(vhost);
5746072b91f9SBrian King 	}
5747072b91f9SBrian King 
5748072b91f9SBrian King 	ibmvfc_dbg(vhost, "ibmvfc kthread exiting...\n");
5749072b91f9SBrian King 	return 0;
5750072b91f9SBrian King }
5751072b91f9SBrian King 
5752072b91f9SBrian King /**
5753f8968665STyrel Datwyler  * ibmvfc_alloc_queue - Allocate queue
5754f8968665STyrel Datwyler  * @vhost:	ibmvfc host struct
5755f8968665STyrel Datwyler  * @queue:	ibmvfc queue to allocate
5756f8968665STyrel Datwyler  * @fmt:	queue format to allocate
5757f8968665STyrel Datwyler  *
5758f8968665STyrel Datwyler  * Returns:
5759f8968665STyrel Datwyler  *	0 on success / non-zero on failure
5760f8968665STyrel Datwyler  **/
ibmvfc_alloc_queue(struct ibmvfc_host * vhost,struct ibmvfc_queue * queue,enum ibmvfc_msg_fmt fmt)5761f8968665STyrel Datwyler static int ibmvfc_alloc_queue(struct ibmvfc_host *vhost,
5762f8968665STyrel Datwyler 			      struct ibmvfc_queue *queue,
5763f8968665STyrel Datwyler 			      enum ibmvfc_msg_fmt fmt)
5764f8968665STyrel Datwyler {
5765f8968665STyrel Datwyler 	struct device *dev = vhost->dev;
5766f8968665STyrel Datwyler 	size_t fmt_size;
5767bb35ecb2STyrel Datwyler 	unsigned int pool_size = 0;
5768f8968665STyrel Datwyler 
5769f8968665STyrel Datwyler 	ENTER;
577057e80e0bSTyrel Datwyler 	spin_lock_init(&queue->_lock);
577157e80e0bSTyrel Datwyler 	queue->q_lock = &queue->_lock;
577257e80e0bSTyrel Datwyler 
5773f8968665STyrel Datwyler 	switch (fmt) {
5774f8968665STyrel Datwyler 	case IBMVFC_CRQ_FMT:
5775f8968665STyrel Datwyler 		fmt_size = sizeof(*queue->msgs.crq);
5776bb35ecb2STyrel Datwyler 		pool_size = max_requests + IBMVFC_NUM_INTERNAL_REQ;
5777f8968665STyrel Datwyler 		break;
5778f8968665STyrel Datwyler 	case IBMVFC_ASYNC_FMT:
5779f8968665STyrel Datwyler 		fmt_size = sizeof(*queue->msgs.async);
5780f8968665STyrel Datwyler 		break;
57813034ebe2STyrel Datwyler 	case IBMVFC_SUB_CRQ_FMT:
57823034ebe2STyrel Datwyler 		fmt_size = sizeof(*queue->msgs.scrq);
57833034ebe2STyrel Datwyler 		/* We need one extra event for Cancel Commands */
57843034ebe2STyrel Datwyler 		pool_size = max_requests + 1;
57853034ebe2STyrel Datwyler 		break;
5786f8968665STyrel Datwyler 	default:
5787f8968665STyrel Datwyler 		dev_warn(dev, "Unknown command/response queue message format: %d\n", fmt);
5788f8968665STyrel Datwyler 		return -EINVAL;
5789f8968665STyrel Datwyler 	}
5790f8968665STyrel Datwyler 
5791bb35ecb2STyrel Datwyler 	if (ibmvfc_init_event_pool(vhost, queue, pool_size)) {
5792bb35ecb2STyrel Datwyler 		dev_err(dev, "Couldn't initialize event pool.\n");
5793bb35ecb2STyrel Datwyler 		return -ENOMEM;
5794bb35ecb2STyrel Datwyler 	}
5795bb35ecb2STyrel Datwyler 
5796f8968665STyrel Datwyler 	queue->msgs.handle = (void *)get_zeroed_page(GFP_KERNEL);
5797f8968665STyrel Datwyler 	if (!queue->msgs.handle)
5798f8968665STyrel Datwyler 		return -ENOMEM;
5799f8968665STyrel Datwyler 
5800f8968665STyrel Datwyler 	queue->msg_token = dma_map_single(dev, queue->msgs.handle, PAGE_SIZE,
5801f8968665STyrel Datwyler 					  DMA_BIDIRECTIONAL);
5802f8968665STyrel Datwyler 
5803f8968665STyrel Datwyler 	if (dma_mapping_error(dev, queue->msg_token)) {
5804f8968665STyrel Datwyler 		free_page((unsigned long)queue->msgs.handle);
5805f8968665STyrel Datwyler 		queue->msgs.handle = NULL;
5806f8968665STyrel Datwyler 		return -ENOMEM;
5807f8968665STyrel Datwyler 	}
5808f8968665STyrel Datwyler 
5809f8968665STyrel Datwyler 	queue->cur = 0;
5810f8968665STyrel Datwyler 	queue->fmt = fmt;
5811f8968665STyrel Datwyler 	queue->size = PAGE_SIZE / fmt_size;
5812aeaadcdeSTyrel Datwyler 
5813aeaadcdeSTyrel Datwyler 	queue->vhost = vhost;
5814f8968665STyrel Datwyler 	return 0;
5815f8968665STyrel Datwyler }
5816f8968665STyrel Datwyler 
5817f8968665STyrel Datwyler /**
5818072b91f9SBrian King  * ibmvfc_init_crq - Initializes and registers CRQ with hypervisor
5819072b91f9SBrian King  * @vhost:	ibmvfc host struct
5820072b91f9SBrian King  *
5821072b91f9SBrian King  * Allocates a page for messages, maps it for dma, and registers
5822072b91f9SBrian King  * the crq with the hypervisor.
5823072b91f9SBrian King  *
5824072b91f9SBrian King  * Return value:
5825072b91f9SBrian King  *	zero on success / other on failure
5826072b91f9SBrian King  **/
ibmvfc_init_crq(struct ibmvfc_host * vhost)5827072b91f9SBrian King static int ibmvfc_init_crq(struct ibmvfc_host *vhost)
5828072b91f9SBrian King {
5829072b91f9SBrian King 	int rc, retrc = -ENOMEM;
5830072b91f9SBrian King 	struct device *dev = vhost->dev;
5831072b91f9SBrian King 	struct vio_dev *vdev = to_vio_dev(dev);
5832f8968665STyrel Datwyler 	struct ibmvfc_queue *crq = &vhost->crq;
5833072b91f9SBrian King 
5834072b91f9SBrian King 	ENTER;
5835f8968665STyrel Datwyler 	if (ibmvfc_alloc_queue(vhost, crq, IBMVFC_CRQ_FMT))
5836072b91f9SBrian King 		return -ENOMEM;
5837072b91f9SBrian King 
5838072b91f9SBrian King 	retrc = rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
5839072b91f9SBrian King 					crq->msg_token, PAGE_SIZE);
5840072b91f9SBrian King 
5841072b91f9SBrian King 	if (rc == H_RESOURCE)
5842072b91f9SBrian King 		/* maybe kexecing and resource is busy. try a reset */
5843072b91f9SBrian King 		retrc = rc = ibmvfc_reset_crq(vhost);
5844072b91f9SBrian King 
5845072b91f9SBrian King 	if (rc == H_CLOSED)
5846072b91f9SBrian King 		dev_warn(dev, "Partner adapter not ready\n");
5847072b91f9SBrian King 	else if (rc) {
5848072b91f9SBrian King 		dev_warn(dev, "Error %d opening adapter\n", rc);
5849072b91f9SBrian King 		goto reg_crq_failed;
5850072b91f9SBrian King 	}
5851072b91f9SBrian King 
5852072b91f9SBrian King 	retrc = 0;
5853072b91f9SBrian King 
5854039a0898SBrian King 	tasklet_init(&vhost->tasklet, (void *)ibmvfc_tasklet, (unsigned long)vhost);
5855039a0898SBrian King 
5856072b91f9SBrian King 	if ((rc = request_irq(vdev->irq, ibmvfc_interrupt, 0, IBMVFC_NAME, vhost))) {
5857072b91f9SBrian King 		dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", vdev->irq, rc);
5858072b91f9SBrian King 		goto req_irq_failed;
5859072b91f9SBrian King 	}
5860072b91f9SBrian King 
5861072b91f9SBrian King 	if ((rc = vio_enable_interrupts(vdev))) {
5862072b91f9SBrian King 		dev_err(dev, "Error %d enabling interrupts\n", rc);
5863072b91f9SBrian King 		goto req_irq_failed;
5864072b91f9SBrian King 	}
5865072b91f9SBrian King 
5866072b91f9SBrian King 	LEAVE;
5867072b91f9SBrian King 	return retrc;
5868072b91f9SBrian King 
5869072b91f9SBrian King req_irq_failed:
5870039a0898SBrian King 	tasklet_kill(&vhost->tasklet);
5871072b91f9SBrian King 	do {
5872072b91f9SBrian King 		rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
5873072b91f9SBrian King 	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
5874072b91f9SBrian King reg_crq_failed:
5875f8968665STyrel Datwyler 	ibmvfc_free_queue(vhost, crq);
5876072b91f9SBrian King 	return retrc;
5877072b91f9SBrian King }
5878072b91f9SBrian King 
ibmvfc_register_scsi_channel(struct ibmvfc_host * vhost,int index)58793034ebe2STyrel Datwyler static int ibmvfc_register_scsi_channel(struct ibmvfc_host *vhost,
58803034ebe2STyrel Datwyler 				  int index)
58813034ebe2STyrel Datwyler {
58823034ebe2STyrel Datwyler 	struct device *dev = vhost->dev;
58833034ebe2STyrel Datwyler 	struct vio_dev *vdev = to_vio_dev(dev);
58843034ebe2STyrel Datwyler 	struct ibmvfc_queue *scrq = &vhost->scsi_scrqs.scrqs[index];
58853034ebe2STyrel Datwyler 	int rc = -ENOMEM;
58863034ebe2STyrel Datwyler 
58873034ebe2STyrel Datwyler 	ENTER;
58883034ebe2STyrel Datwyler 
58893034ebe2STyrel Datwyler 	rc = h_reg_sub_crq(vdev->unit_address, scrq->msg_token, PAGE_SIZE,
58903034ebe2STyrel Datwyler 			   &scrq->cookie, &scrq->hw_irq);
58913034ebe2STyrel Datwyler 
58922162dc23STyrel Datwyler 	/* H_CLOSED indicates successful register, but no CRQ partner */
58932162dc23STyrel Datwyler 	if (rc && rc != H_CLOSED) {
58943034ebe2STyrel Datwyler 		dev_warn(dev, "Error registering sub-crq: %d\n", rc);
58953034ebe2STyrel Datwyler 		if (rc == H_PARAMETER)
58963034ebe2STyrel Datwyler 			dev_warn_once(dev, "Firmware may not support MQ\n");
58973034ebe2STyrel Datwyler 		goto reg_failed;
58983034ebe2STyrel Datwyler 	}
58993034ebe2STyrel Datwyler 
590039e461fdSTyrel Datwyler 	scrq->irq = irq_create_mapping(NULL, scrq->hw_irq);
590139e461fdSTyrel Datwyler 
590239e461fdSTyrel Datwyler 	if (!scrq->irq) {
590339e461fdSTyrel Datwyler 		rc = -EINVAL;
590439e461fdSTyrel Datwyler 		dev_err(dev, "Error mapping sub-crq[%d] irq\n", index);
590539e461fdSTyrel Datwyler 		goto irq_failed;
590639e461fdSTyrel Datwyler 	}
590739e461fdSTyrel Datwyler 
590839e461fdSTyrel Datwyler 	snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-scsi%d",
590939e461fdSTyrel Datwyler 		 vdev->unit_address, index);
591039e461fdSTyrel Datwyler 	rc = request_irq(scrq->irq, ibmvfc_interrupt_scsi, 0, scrq->name, scrq);
591139e461fdSTyrel Datwyler 
591239e461fdSTyrel Datwyler 	if (rc) {
591339e461fdSTyrel Datwyler 		dev_err(dev, "Couldn't register sub-crq[%d] irq\n", index);
591439e461fdSTyrel Datwyler 		irq_dispose_mapping(scrq->irq);
591539e461fdSTyrel Datwyler 		goto irq_failed;
591639e461fdSTyrel Datwyler 	}
591739e461fdSTyrel Datwyler 
59183034ebe2STyrel Datwyler 	scrq->hwq_id = index;
59193034ebe2STyrel Datwyler 
59203034ebe2STyrel Datwyler 	LEAVE;
59213034ebe2STyrel Datwyler 	return 0;
59223034ebe2STyrel Datwyler 
592339e461fdSTyrel Datwyler irq_failed:
592439e461fdSTyrel Datwyler 	do {
59250217a272STyrel Datwyler 		rc = plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address, scrq->cookie);
5926c1fed82dSTyrel Datwyler 	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
59273034ebe2STyrel Datwyler reg_failed:
59283034ebe2STyrel Datwyler 	LEAVE;
59293034ebe2STyrel Datwyler 	return rc;
59303034ebe2STyrel Datwyler }
59313034ebe2STyrel Datwyler 
ibmvfc_deregister_scsi_channel(struct ibmvfc_host * vhost,int index)59323034ebe2STyrel Datwyler static void ibmvfc_deregister_scsi_channel(struct ibmvfc_host *vhost, int index)
59333034ebe2STyrel Datwyler {
59343034ebe2STyrel Datwyler 	struct device *dev = vhost->dev;
59353034ebe2STyrel Datwyler 	struct vio_dev *vdev = to_vio_dev(dev);
59363034ebe2STyrel Datwyler 	struct ibmvfc_queue *scrq = &vhost->scsi_scrqs.scrqs[index];
59373034ebe2STyrel Datwyler 	long rc;
59383034ebe2STyrel Datwyler 
59393034ebe2STyrel Datwyler 	ENTER;
59403034ebe2STyrel Datwyler 
594139e461fdSTyrel Datwyler 	free_irq(scrq->irq, scrq);
594239e461fdSTyrel Datwyler 	irq_dispose_mapping(scrq->irq);
59435cf52964STyrel Datwyler 	scrq->irq = 0;
594439e461fdSTyrel Datwyler 
59453034ebe2STyrel Datwyler 	do {
59463034ebe2STyrel Datwyler 		rc = plpar_hcall_norets(H_FREE_SUB_CRQ, vdev->unit_address,
59473034ebe2STyrel Datwyler 					scrq->cookie);
59483034ebe2STyrel Datwyler 	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
59493034ebe2STyrel Datwyler 
59503034ebe2STyrel Datwyler 	if (rc)
59513034ebe2STyrel Datwyler 		dev_err(dev, "Failed to free sub-crq[%d]: rc=%ld\n", index, rc);
59523034ebe2STyrel Datwyler 
595372ea7fe0STyrel Datwyler 	/* Clean out the queue */
595472ea7fe0STyrel Datwyler 	memset(scrq->msgs.crq, 0, PAGE_SIZE);
595572ea7fe0STyrel Datwyler 	scrq->cur = 0;
595672ea7fe0STyrel Datwyler 
595772ea7fe0STyrel Datwyler 	LEAVE;
595872ea7fe0STyrel Datwyler }
595972ea7fe0STyrel Datwyler 
ibmvfc_reg_sub_crqs(struct ibmvfc_host * vhost)596072ea7fe0STyrel Datwyler static void ibmvfc_reg_sub_crqs(struct ibmvfc_host *vhost)
596172ea7fe0STyrel Datwyler {
596272ea7fe0STyrel Datwyler 	int i, j;
596372ea7fe0STyrel Datwyler 
596472ea7fe0STyrel Datwyler 	ENTER;
596572ea7fe0STyrel Datwyler 	if (!vhost->mq_enabled || !vhost->scsi_scrqs.scrqs)
596672ea7fe0STyrel Datwyler 		return;
596772ea7fe0STyrel Datwyler 
596872ea7fe0STyrel Datwyler 	for (i = 0; i < nr_scsi_hw_queues; i++) {
596972ea7fe0STyrel Datwyler 		if (ibmvfc_register_scsi_channel(vhost, i)) {
597072ea7fe0STyrel Datwyler 			for (j = i; j > 0; j--)
597172ea7fe0STyrel Datwyler 				ibmvfc_deregister_scsi_channel(vhost, j - 1);
597272ea7fe0STyrel Datwyler 			vhost->do_enquiry = 0;
597372ea7fe0STyrel Datwyler 			return;
597472ea7fe0STyrel Datwyler 		}
597572ea7fe0STyrel Datwyler 	}
597672ea7fe0STyrel Datwyler 
597772ea7fe0STyrel Datwyler 	LEAVE;
597872ea7fe0STyrel Datwyler }
597972ea7fe0STyrel Datwyler 
ibmvfc_dereg_sub_crqs(struct ibmvfc_host * vhost)598072ea7fe0STyrel Datwyler static void ibmvfc_dereg_sub_crqs(struct ibmvfc_host *vhost)
598172ea7fe0STyrel Datwyler {
598272ea7fe0STyrel Datwyler 	int i;
598372ea7fe0STyrel Datwyler 
598472ea7fe0STyrel Datwyler 	ENTER;
598572ea7fe0STyrel Datwyler 	if (!vhost->mq_enabled || !vhost->scsi_scrqs.scrqs)
598672ea7fe0STyrel Datwyler 		return;
598772ea7fe0STyrel Datwyler 
598872ea7fe0STyrel Datwyler 	for (i = 0; i < nr_scsi_hw_queues; i++)
598972ea7fe0STyrel Datwyler 		ibmvfc_deregister_scsi_channel(vhost, i);
599072ea7fe0STyrel Datwyler 
59913034ebe2STyrel Datwyler 	LEAVE;
59923034ebe2STyrel Datwyler }
59933034ebe2STyrel Datwyler 
ibmvfc_init_sub_crqs(struct ibmvfc_host * vhost)5994443cc4b4STyrel Datwyler static void ibmvfc_init_sub_crqs(struct ibmvfc_host *vhost)
59953034ebe2STyrel Datwyler {
599672ea7fe0STyrel Datwyler 	struct ibmvfc_queue *scrq;
59973034ebe2STyrel Datwyler 	int i, j;
59983034ebe2STyrel Datwyler 
59993034ebe2STyrel Datwyler 	ENTER;
6000443cc4b4STyrel Datwyler 	if (!vhost->mq_enabled)
6001443cc4b4STyrel Datwyler 		return;
60023034ebe2STyrel Datwyler 
6003032d1900STyrel Datwyler 	vhost->scsi_scrqs.scrqs = kcalloc(nr_scsi_hw_queues,
60043034ebe2STyrel Datwyler 					  sizeof(*vhost->scsi_scrqs.scrqs),
60053034ebe2STyrel Datwyler 					  GFP_KERNEL);
6006443cc4b4STyrel Datwyler 	if (!vhost->scsi_scrqs.scrqs) {
6007443cc4b4STyrel Datwyler 		vhost->do_enquiry = 0;
6008443cc4b4STyrel Datwyler 		return;
6009443cc4b4STyrel Datwyler 	}
60103034ebe2STyrel Datwyler 
6011032d1900STyrel Datwyler 	for (i = 0; i < nr_scsi_hw_queues; i++) {
601272ea7fe0STyrel Datwyler 		scrq = &vhost->scsi_scrqs.scrqs[i];
601372ea7fe0STyrel Datwyler 		if (ibmvfc_alloc_queue(vhost, scrq, IBMVFC_SUB_CRQ_FMT)) {
601472ea7fe0STyrel Datwyler 			for (j = i; j > 0; j--) {
601572ea7fe0STyrel Datwyler 				scrq = &vhost->scsi_scrqs.scrqs[j - 1];
601672ea7fe0STyrel Datwyler 				ibmvfc_free_queue(vhost, scrq);
601772ea7fe0STyrel Datwyler 			}
60183034ebe2STyrel Datwyler 			kfree(vhost->scsi_scrqs.scrqs);
60193034ebe2STyrel Datwyler 			vhost->scsi_scrqs.scrqs = NULL;
60203034ebe2STyrel Datwyler 			vhost->scsi_scrqs.active_queues = 0;
6021443cc4b4STyrel Datwyler 			vhost->do_enquiry = 0;
602272ea7fe0STyrel Datwyler 			vhost->mq_enabled = 0;
602372ea7fe0STyrel Datwyler 			return;
60243034ebe2STyrel Datwyler 		}
60253034ebe2STyrel Datwyler 	}
60263034ebe2STyrel Datwyler 
602772ea7fe0STyrel Datwyler 	ibmvfc_reg_sub_crqs(vhost);
602872ea7fe0STyrel Datwyler 
60293034ebe2STyrel Datwyler 	LEAVE;
60303034ebe2STyrel Datwyler }
60313034ebe2STyrel Datwyler 
ibmvfc_release_sub_crqs(struct ibmvfc_host * vhost)60323034ebe2STyrel Datwyler static void ibmvfc_release_sub_crqs(struct ibmvfc_host *vhost)
60333034ebe2STyrel Datwyler {
603472ea7fe0STyrel Datwyler 	struct ibmvfc_queue *scrq;
60353034ebe2STyrel Datwyler 	int i;
60363034ebe2STyrel Datwyler 
60373034ebe2STyrel Datwyler 	ENTER;
60383034ebe2STyrel Datwyler 	if (!vhost->scsi_scrqs.scrqs)
60393034ebe2STyrel Datwyler 		return;
60403034ebe2STyrel Datwyler 
604172ea7fe0STyrel Datwyler 	ibmvfc_dereg_sub_crqs(vhost);
604272ea7fe0STyrel Datwyler 
604372ea7fe0STyrel Datwyler 	for (i = 0; i < nr_scsi_hw_queues; i++) {
604472ea7fe0STyrel Datwyler 		scrq = &vhost->scsi_scrqs.scrqs[i];
604572ea7fe0STyrel Datwyler 		ibmvfc_free_queue(vhost, scrq);
604672ea7fe0STyrel Datwyler 	}
60473034ebe2STyrel Datwyler 
60483034ebe2STyrel Datwyler 	kfree(vhost->scsi_scrqs.scrqs);
60493034ebe2STyrel Datwyler 	vhost->scsi_scrqs.scrqs = NULL;
60503034ebe2STyrel Datwyler 	vhost->scsi_scrqs.active_queues = 0;
60513034ebe2STyrel Datwyler 	LEAVE;
60523034ebe2STyrel Datwyler }
60533034ebe2STyrel Datwyler 
6054072b91f9SBrian King /**
6055072b91f9SBrian King  * ibmvfc_free_mem - Free memory for vhost
6056072b91f9SBrian King  * @vhost:	ibmvfc host struct
6057072b91f9SBrian King  *
6058072b91f9SBrian King  * Return value:
6059072b91f9SBrian King  * 	none
6060072b91f9SBrian King  **/
ibmvfc_free_mem(struct ibmvfc_host * vhost)6061072b91f9SBrian King static void ibmvfc_free_mem(struct ibmvfc_host *vhost)
6062072b91f9SBrian King {
6063f8968665STyrel Datwyler 	struct ibmvfc_queue *async_q = &vhost->async_crq;
6064072b91f9SBrian King 
6065072b91f9SBrian King 	ENTER;
6066072b91f9SBrian King 	mempool_destroy(vhost->tgt_pool);
6067072b91f9SBrian King 	kfree(vhost->trace);
6068072b91f9SBrian King 	dma_free_coherent(vhost->dev, vhost->disc_buf_sz, vhost->disc_buf,
6069072b91f9SBrian King 			  vhost->disc_buf_dma);
6070072b91f9SBrian King 	dma_free_coherent(vhost->dev, sizeof(*vhost->login_buf),
6071072b91f9SBrian King 			  vhost->login_buf, vhost->login_buf_dma);
6072febb0cc8STyrel Datwyler 	dma_free_coherent(vhost->dev, sizeof(*vhost->channel_setup_buf),
6073febb0cc8STyrel Datwyler 			  vhost->channel_setup_buf, vhost->channel_setup_dma);
6074072b91f9SBrian King 	dma_pool_destroy(vhost->sg_pool);
6075f8968665STyrel Datwyler 	ibmvfc_free_queue(vhost, async_q);
6076072b91f9SBrian King 	LEAVE;
6077072b91f9SBrian King }
6078072b91f9SBrian King 
6079072b91f9SBrian King /**
6080072b91f9SBrian King  * ibmvfc_alloc_mem - Allocate memory for vhost
6081072b91f9SBrian King  * @vhost:	ibmvfc host struct
6082072b91f9SBrian King  *
6083072b91f9SBrian King  * Return value:
6084072b91f9SBrian King  * 	0 on success / non-zero on failure
6085072b91f9SBrian King  **/
ibmvfc_alloc_mem(struct ibmvfc_host * vhost)6086072b91f9SBrian King static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
6087072b91f9SBrian King {
6088f8968665STyrel Datwyler 	struct ibmvfc_queue *async_q = &vhost->async_crq;
6089072b91f9SBrian King 	struct device *dev = vhost->dev;
6090072b91f9SBrian King 
6091072b91f9SBrian King 	ENTER;
6092f8968665STyrel Datwyler 	if (ibmvfc_alloc_queue(vhost, async_q, IBMVFC_ASYNC_FMT)) {
6093f8968665STyrel Datwyler 		dev_err(dev, "Couldn't allocate/map async queue.\n");
6094072b91f9SBrian King 		goto nomem;
6095072b91f9SBrian King 	}
6096072b91f9SBrian King 
6097072b91f9SBrian King 	vhost->sg_pool = dma_pool_create(IBMVFC_NAME, dev,
6098072b91f9SBrian King 					 SG_ALL * sizeof(struct srp_direct_buf),
6099072b91f9SBrian King 					 sizeof(struct srp_direct_buf), 0);
6100072b91f9SBrian King 
6101072b91f9SBrian King 	if (!vhost->sg_pool) {
6102072b91f9SBrian King 		dev_err(dev, "Failed to allocate sg pool\n");
6103072b91f9SBrian King 		goto unmap_async_crq;
6104072b91f9SBrian King 	}
6105072b91f9SBrian King 
6106072b91f9SBrian King 	vhost->login_buf = dma_alloc_coherent(dev, sizeof(*vhost->login_buf),
6107072b91f9SBrian King 					      &vhost->login_buf_dma, GFP_KERNEL);
6108072b91f9SBrian King 
6109072b91f9SBrian King 	if (!vhost->login_buf) {
6110072b91f9SBrian King 		dev_err(dev, "Couldn't allocate NPIV login buffer\n");
6111072b91f9SBrian King 		goto free_sg_pool;
6112072b91f9SBrian King 	}
6113072b91f9SBrian King 
61144b29cb61SBrian King 	vhost->disc_buf_sz = sizeof(*vhost->disc_buf) * max_targets;
6115072b91f9SBrian King 	vhost->disc_buf = dma_alloc_coherent(dev, vhost->disc_buf_sz,
6116072b91f9SBrian King 					     &vhost->disc_buf_dma, GFP_KERNEL);
6117072b91f9SBrian King 
6118072b91f9SBrian King 	if (!vhost->disc_buf) {
6119072b91f9SBrian King 		dev_err(dev, "Couldn't allocate Discover Targets buffer\n");
6120072b91f9SBrian King 		goto free_login_buffer;
6121072b91f9SBrian King 	}
6122072b91f9SBrian King 
6123072b91f9SBrian King 	vhost->trace = kcalloc(IBMVFC_NUM_TRACE_ENTRIES,
6124072b91f9SBrian King 			       sizeof(struct ibmvfc_trace_entry), GFP_KERNEL);
612557e80e0bSTyrel Datwyler 	atomic_set(&vhost->trace_index, -1);
6126072b91f9SBrian King 
6127072b91f9SBrian King 	if (!vhost->trace)
6128072b91f9SBrian King 		goto free_disc_buffer;
6129072b91f9SBrian King 
6130d6886692SSage Weil 	vhost->tgt_pool = mempool_create_kmalloc_pool(IBMVFC_TGT_MEMPOOL_SZ,
6131072b91f9SBrian King 						      sizeof(struct ibmvfc_target));
6132072b91f9SBrian King 
6133072b91f9SBrian King 	if (!vhost->tgt_pool) {
6134072b91f9SBrian King 		dev_err(dev, "Couldn't allocate target memory pool\n");
6135072b91f9SBrian King 		goto free_trace;
6136072b91f9SBrian King 	}
6137072b91f9SBrian King 
6138e95eef3fSTyrel Datwyler 	vhost->channel_setup_buf = dma_alloc_coherent(dev, sizeof(*vhost->channel_setup_buf),
6139e95eef3fSTyrel Datwyler 						      &vhost->channel_setup_dma,
6140e95eef3fSTyrel Datwyler 						      GFP_KERNEL);
6141e95eef3fSTyrel Datwyler 
6142e95eef3fSTyrel Datwyler 	if (!vhost->channel_setup_buf) {
6143e95eef3fSTyrel Datwyler 		dev_err(dev, "Couldn't allocate Channel Setup buffer\n");
6144e95eef3fSTyrel Datwyler 		goto free_tgt_pool;
6145e95eef3fSTyrel Datwyler 	}
6146e95eef3fSTyrel Datwyler 
6147072b91f9SBrian King 	LEAVE;
6148072b91f9SBrian King 	return 0;
6149072b91f9SBrian King 
6150e95eef3fSTyrel Datwyler free_tgt_pool:
6151e95eef3fSTyrel Datwyler 	mempool_destroy(vhost->tgt_pool);
6152072b91f9SBrian King free_trace:
6153072b91f9SBrian King 	kfree(vhost->trace);
6154072b91f9SBrian King free_disc_buffer:
6155072b91f9SBrian King 	dma_free_coherent(dev, vhost->disc_buf_sz, vhost->disc_buf,
6156072b91f9SBrian King 			  vhost->disc_buf_dma);
6157072b91f9SBrian King free_login_buffer:
6158072b91f9SBrian King 	dma_free_coherent(dev, sizeof(*vhost->login_buf),
6159072b91f9SBrian King 			  vhost->login_buf, vhost->login_buf_dma);
6160072b91f9SBrian King free_sg_pool:
6161072b91f9SBrian King 	dma_pool_destroy(vhost->sg_pool);
6162072b91f9SBrian King unmap_async_crq:
6163f8968665STyrel Datwyler 	ibmvfc_free_queue(vhost, async_q);
6164072b91f9SBrian King nomem:
6165072b91f9SBrian King 	LEAVE;
6166072b91f9SBrian King 	return -ENOMEM;
6167072b91f9SBrian King }
6168072b91f9SBrian King 
6169072b91f9SBrian King /**
617043c8da90SBrian King  * ibmvfc_rport_add_thread - Worker thread for rport adds
617143c8da90SBrian King  * @work:	work struct
617243c8da90SBrian King  *
617343c8da90SBrian King  **/
ibmvfc_rport_add_thread(struct work_struct * work)617443c8da90SBrian King static void ibmvfc_rport_add_thread(struct work_struct *work)
617543c8da90SBrian King {
617643c8da90SBrian King 	struct ibmvfc_host *vhost = container_of(work, struct ibmvfc_host,
617743c8da90SBrian King 						 rport_add_work_q);
617843c8da90SBrian King 	struct ibmvfc_target *tgt;
617943c8da90SBrian King 	struct fc_rport *rport;
618043c8da90SBrian King 	unsigned long flags;
618143c8da90SBrian King 	int did_work;
618243c8da90SBrian King 
618343c8da90SBrian King 	ENTER;
618443c8da90SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
618543c8da90SBrian King 	do {
618643c8da90SBrian King 		did_work = 0;
618743c8da90SBrian King 		if (vhost->state != IBMVFC_ACTIVE)
618843c8da90SBrian King 			break;
618943c8da90SBrian King 
619043c8da90SBrian King 		list_for_each_entry(tgt, &vhost->targets, queue) {
619143c8da90SBrian King 			if (tgt->add_rport) {
619243c8da90SBrian King 				did_work = 1;
619343c8da90SBrian King 				tgt->add_rport = 0;
619443c8da90SBrian King 				kref_get(&tgt->kref);
619543c8da90SBrian King 				rport = tgt->rport;
619643c8da90SBrian King 				if (!rport) {
619743c8da90SBrian King 					spin_unlock_irqrestore(vhost->host->host_lock, flags);
619843c8da90SBrian King 					ibmvfc_tgt_add_rport(tgt);
619943c8da90SBrian King 				} else if (get_device(&rport->dev)) {
620043c8da90SBrian King 					spin_unlock_irqrestore(vhost->host->host_lock, flags);
620143c8da90SBrian King 					tgt_dbg(tgt, "Setting rport roles\n");
620243c8da90SBrian King 					fc_remote_port_rolechg(rport, tgt->ids.roles);
620343c8da90SBrian King 					put_device(&rport->dev);
62040073887aSDan Carpenter 				} else {
62050073887aSDan Carpenter 					spin_unlock_irqrestore(vhost->host->host_lock, flags);
620643c8da90SBrian King 				}
620743c8da90SBrian King 
620843c8da90SBrian King 				kref_put(&tgt->kref, ibmvfc_release_tgt);
620943c8da90SBrian King 				spin_lock_irqsave(vhost->host->host_lock, flags);
621043c8da90SBrian King 				break;
621143c8da90SBrian King 			}
621243c8da90SBrian King 		}
621343c8da90SBrian King 	} while(did_work);
621443c8da90SBrian King 
621543c8da90SBrian King 	if (vhost->state == IBMVFC_ACTIVE)
621643c8da90SBrian King 		vhost->scan_complete = 1;
621743c8da90SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
621843c8da90SBrian King 	LEAVE;
621943c8da90SBrian King }
622043c8da90SBrian King 
622143c8da90SBrian King /**
6222072b91f9SBrian King  * ibmvfc_probe - Adapter hot plug add entry point
6223072b91f9SBrian King  * @vdev:	vio device struct
6224072b91f9SBrian King  * @id:	vio device id struct
6225072b91f9SBrian King  *
6226072b91f9SBrian King  * Return value:
6227072b91f9SBrian King  * 	0 on success / non-zero on failure
6228072b91f9SBrian King  **/
ibmvfc_probe(struct vio_dev * vdev,const struct vio_device_id * id)6229072b91f9SBrian King static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
6230072b91f9SBrian King {
6231072b91f9SBrian King 	struct ibmvfc_host *vhost;
6232072b91f9SBrian King 	struct Scsi_Host *shost;
6233072b91f9SBrian King 	struct device *dev = &vdev->dev;
6234072b91f9SBrian King 	int rc = -ENOMEM;
6235032d1900STyrel Datwyler 	unsigned int max_scsi_queues = IBMVFC_MAX_SCSI_QUEUES;
6236072b91f9SBrian King 
6237072b91f9SBrian King 	ENTER;
6238072b91f9SBrian King 	shost = scsi_host_alloc(&driver_template, sizeof(*vhost));
6239072b91f9SBrian King 	if (!shost) {
6240072b91f9SBrian King 		dev_err(dev, "Couldn't allocate host data\n");
6241072b91f9SBrian King 		goto out;
6242072b91f9SBrian King 	}
6243072b91f9SBrian King 
6244072b91f9SBrian King 	shost->transportt = ibmvfc_transport_template;
6245072b91f9SBrian King 	shost->can_queue = max_requests;
6246072b91f9SBrian King 	shost->max_lun = max_lun;
6247072b91f9SBrian King 	shost->max_id = max_targets;
6248072b91f9SBrian King 	shost->max_sectors = IBMVFC_MAX_SECTORS;
6249072b91f9SBrian King 	shost->max_cmd_len = IBMVFC_MAX_CDB_LEN;
6250072b91f9SBrian King 	shost->unique_id = shost->host_no;
6251032d1900STyrel Datwyler 	shost->nr_hw_queues = mq_enabled ? min(max_scsi_queues, nr_scsi_hw_queues) : 1;
6252072b91f9SBrian King 
6253072b91f9SBrian King 	vhost = shost_priv(shost);
6254072b91f9SBrian King 	INIT_LIST_HEAD(&vhost->targets);
625557e80e0bSTyrel Datwyler 	INIT_LIST_HEAD(&vhost->purge);
6256072b91f9SBrian King 	sprintf(vhost->name, IBMVFC_NAME);
6257072b91f9SBrian King 	vhost->host = shost;
6258072b91f9SBrian King 	vhost->dev = dev;
6259072b91f9SBrian King 	vhost->partition_number = -1;
6260072b91f9SBrian King 	vhost->log_level = log_level;
626110501e1cSBrian King 	vhost->task_set = 1;
62626ae208e5STyrel Datwyler 
6263032d1900STyrel Datwyler 	vhost->mq_enabled = mq_enabled;
6264032d1900STyrel Datwyler 	vhost->client_scsi_channels = min(shost->nr_hw_queues, nr_scsi_channels);
62656ae208e5STyrel Datwyler 	vhost->using_channels = 0;
62666ae208e5STyrel Datwyler 	vhost->do_enquiry = 1;
62677a3795f2SHannes Reinecke 	vhost->scan_timeout = 0;
62686ae208e5STyrel Datwyler 
6269072b91f9SBrian King 	strcpy(vhost->partition_name, "UNKNOWN");
6270072b91f9SBrian King 	init_waitqueue_head(&vhost->work_wait_q);
6271072b91f9SBrian King 	init_waitqueue_head(&vhost->init_wait_q);
627243c8da90SBrian King 	INIT_WORK(&vhost->rport_add_work_q, ibmvfc_rport_add_thread);
6273d31429e1SBrian King 	mutex_init(&vhost->passthru_mutex);
6274072b91f9SBrian King 
6275072b91f9SBrian King 	if ((rc = ibmvfc_alloc_mem(vhost)))
6276072b91f9SBrian King 		goto free_scsi_host;
6277072b91f9SBrian King 
6278072b91f9SBrian King 	vhost->work_thread = kthread_run(ibmvfc_work, vhost, "%s_%d", IBMVFC_NAME,
6279072b91f9SBrian King 					 shost->host_no);
6280072b91f9SBrian King 
6281072b91f9SBrian King 	if (IS_ERR(vhost->work_thread)) {
6282072b91f9SBrian King 		dev_err(dev, "Couldn't create kernel thread: %ld\n",
6283072b91f9SBrian King 			PTR_ERR(vhost->work_thread));
62845e48a084SJing Xiangfeng 		rc = PTR_ERR(vhost->work_thread);
6285072b91f9SBrian King 		goto free_host_mem;
6286072b91f9SBrian King 	}
6287072b91f9SBrian King 
6288072b91f9SBrian King 	if ((rc = ibmvfc_init_crq(vhost))) {
6289072b91f9SBrian King 		dev_err(dev, "Couldn't initialize crq. rc=%d\n", rc);
6290072b91f9SBrian King 		goto kill_kthread;
6291072b91f9SBrian King 	}
6292072b91f9SBrian King 
6293072b91f9SBrian King 	if ((rc = scsi_add_host(shost, dev)))
6294003d91a1STyrel Datwyler 		goto release_crq;
6295072b91f9SBrian King 
6296a5110f29SMike Christie 	fc_host_dev_loss_tmo(shost) = IBMVFC_DEV_LOSS_TMO;
6297a5110f29SMike Christie 
6298072b91f9SBrian King 	if ((rc = ibmvfc_create_trace_file(&shost->shost_dev.kobj,
6299072b91f9SBrian King 					   &ibmvfc_trace_attr))) {
6300072b91f9SBrian King 		dev_err(dev, "Failed to create trace file. rc=%d\n", rc);
6301072b91f9SBrian King 		goto remove_shost;
6302072b91f9SBrian King 	}
6303072b91f9SBrian King 
6304443cc4b4STyrel Datwyler 	ibmvfc_init_sub_crqs(vhost);
63053034ebe2STyrel Datwyler 
6306d31429e1SBrian King 	if (shost_to_fc_host(shost)->rqst_q)
63078a78362cSMartin K. Petersen 		blk_queue_max_segments(shost_to_fc_host(shost)->rqst_q, 1);
6308072b91f9SBrian King 	dev_set_drvdata(dev, vhost);
6309072b91f9SBrian King 	spin_lock(&ibmvfc_driver_lock);
6310072b91f9SBrian King 	list_add_tail(&vhost->queue, &ibmvfc_head);
6311072b91f9SBrian King 	spin_unlock(&ibmvfc_driver_lock);
6312072b91f9SBrian King 
6313072b91f9SBrian King 	ibmvfc_send_crq_init(vhost);
6314072b91f9SBrian King 	scsi_scan_host(shost);
6315072b91f9SBrian King 	return 0;
6316072b91f9SBrian King 
6317072b91f9SBrian King remove_shost:
6318072b91f9SBrian King 	scsi_remove_host(shost);
6319072b91f9SBrian King release_crq:
6320072b91f9SBrian King 	ibmvfc_release_crq_queue(vhost);
6321072b91f9SBrian King kill_kthread:
6322072b91f9SBrian King 	kthread_stop(vhost->work_thread);
6323072b91f9SBrian King free_host_mem:
6324072b91f9SBrian King 	ibmvfc_free_mem(vhost);
6325072b91f9SBrian King free_scsi_host:
6326072b91f9SBrian King 	scsi_host_put(shost);
6327072b91f9SBrian King out:
6328072b91f9SBrian King 	LEAVE;
6329072b91f9SBrian King 	return rc;
6330072b91f9SBrian King }
6331072b91f9SBrian King 
6332072b91f9SBrian King /**
6333072b91f9SBrian King  * ibmvfc_remove - Adapter hot plug remove entry point
6334072b91f9SBrian King  * @vdev:	vio device struct
6335072b91f9SBrian King  *
6336072b91f9SBrian King  * Return value:
6337072b91f9SBrian King  * 	0
6338072b91f9SBrian King  **/
ibmvfc_remove(struct vio_dev * vdev)6339386a966fSUwe Kleine-König static void ibmvfc_remove(struct vio_dev *vdev)
6340072b91f9SBrian King {
6341072b91f9SBrian King 	struct ibmvfc_host *vhost = dev_get_drvdata(&vdev->dev);
634257e80e0bSTyrel Datwyler 	LIST_HEAD(purge);
6343072b91f9SBrian King 	unsigned long flags;
6344072b91f9SBrian King 
6345072b91f9SBrian King 	ENTER;
6346072b91f9SBrian King 	ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr);
634770431105SBrian King 
634870431105SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
63492d0da2a4SBrian King 	ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
635070431105SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
635170431105SBrian King 
63522d0da2a4SBrian King 	ibmvfc_wait_while_resetting(vhost);
6353072b91f9SBrian King 	kthread_stop(vhost->work_thread);
6354072b91f9SBrian King 	fc_remove_host(vhost->host);
6355072b91f9SBrian King 	scsi_remove_host(vhost->host);
6356072b91f9SBrian King 
6357072b91f9SBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
6358072b91f9SBrian King 	ibmvfc_purge_requests(vhost, DID_ERROR);
635957e80e0bSTyrel Datwyler 	list_splice_init(&vhost->purge, &purge);
6360072b91f9SBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
63611f4a4a19STyrel Datwyler 	ibmvfc_complete_purge(&purge);
63623034ebe2STyrel Datwyler 	ibmvfc_release_sub_crqs(vhost);
6363003d91a1STyrel Datwyler 	ibmvfc_release_crq_queue(vhost);
6364072b91f9SBrian King 
6365072b91f9SBrian King 	ibmvfc_free_mem(vhost);
6366072b91f9SBrian King 	spin_lock(&ibmvfc_driver_lock);
6367072b91f9SBrian King 	list_del(&vhost->queue);
6368072b91f9SBrian King 	spin_unlock(&ibmvfc_driver_lock);
6369072b91f9SBrian King 	scsi_host_put(vhost->host);
6370072b91f9SBrian King 	LEAVE;
6371072b91f9SBrian King }
6372072b91f9SBrian King 
637339c1ffecSBrian King /**
6374b0f4d4cfSBrian King  * ibmvfc_resume - Resume from suspend
6375b0f4d4cfSBrian King  * @dev:	device struct
6376b0f4d4cfSBrian King  *
6377b0f4d4cfSBrian King  * We may have lost an interrupt across suspend/resume, so kick the
6378b0f4d4cfSBrian King  * interrupt handler
6379b0f4d4cfSBrian King  *
6380b0f4d4cfSBrian King  */
ibmvfc_resume(struct device * dev)6381b0f4d4cfSBrian King static int ibmvfc_resume(struct device *dev)
6382b0f4d4cfSBrian King {
6383b0f4d4cfSBrian King 	unsigned long flags;
6384b0f4d4cfSBrian King 	struct ibmvfc_host *vhost = dev_get_drvdata(dev);
6385b0f4d4cfSBrian King 	struct vio_dev *vdev = to_vio_dev(dev);
6386b0f4d4cfSBrian King 
6387b0f4d4cfSBrian King 	spin_lock_irqsave(vhost->host->host_lock, flags);
6388b0f4d4cfSBrian King 	vio_disable_interrupts(vdev);
6389b0f4d4cfSBrian King 	tasklet_schedule(&vhost->tasklet);
6390b0f4d4cfSBrian King 	spin_unlock_irqrestore(vhost->host->host_lock, flags);
6391b0f4d4cfSBrian King 	return 0;
6392b0f4d4cfSBrian King }
6393b0f4d4cfSBrian King 
6394b0f4d4cfSBrian King /**
639539c1ffecSBrian King  * ibmvfc_get_desired_dma - Calculate DMA resources needed by the driver
639639c1ffecSBrian King  * @vdev:	vio device struct
639739c1ffecSBrian King  *
639839c1ffecSBrian King  * Return value:
639939c1ffecSBrian King  *	Number of bytes the driver will need to DMA map at the same time in
640039c1ffecSBrian King  *	order to perform well.
640139c1ffecSBrian King  */
ibmvfc_get_desired_dma(struct vio_dev * vdev)640239c1ffecSBrian King static unsigned long ibmvfc_get_desired_dma(struct vio_dev *vdev)
640339c1ffecSBrian King {
640439c1ffecSBrian King 	unsigned long pool_dma = max_requests * sizeof(union ibmvfc_iu);
640539c1ffecSBrian King 	return pool_dma + ((512 * 1024) * driver_template.cmd_per_lun);
640639c1ffecSBrian King }
640739c1ffecSBrian King 
6408e4df3eaaSArvind Yadav static const struct vio_device_id ibmvfc_device_table[] = {
6409072b91f9SBrian King 	{"fcp", "IBM,vfc-client"},
6410072b91f9SBrian King 	{ "", "" }
6411072b91f9SBrian King };
6412072b91f9SBrian King MODULE_DEVICE_TABLE(vio, ibmvfc_device_table);
6413072b91f9SBrian King 
6414e4a80c31SArvind Yadav static const struct dev_pm_ops ibmvfc_pm_ops = {
6415b0f4d4cfSBrian King 	.resume = ibmvfc_resume
6416b0f4d4cfSBrian King };
6417b0f4d4cfSBrian King 
6418072b91f9SBrian King static struct vio_driver ibmvfc_driver = {
6419072b91f9SBrian King 	.id_table = ibmvfc_device_table,
6420072b91f9SBrian King 	.probe = ibmvfc_probe,
6421072b91f9SBrian King 	.remove = ibmvfc_remove,
642239c1ffecSBrian King 	.get_desired_dma = ibmvfc_get_desired_dma,
6423072b91f9SBrian King 	.name = IBMVFC_NAME,
6424b0f4d4cfSBrian King 	.pm = &ibmvfc_pm_ops,
6425072b91f9SBrian King };
6426072b91f9SBrian King 
6427072b91f9SBrian King static struct fc_function_template ibmvfc_transport_functions = {
6428072b91f9SBrian King 	.show_host_fabric_name = 1,
6429072b91f9SBrian King 	.show_host_node_name = 1,
6430072b91f9SBrian King 	.show_host_port_name = 1,
6431072b91f9SBrian King 	.show_host_supported_classes = 1,
6432072b91f9SBrian King 	.show_host_port_type = 1,
6433072b91f9SBrian King 	.show_host_port_id = 1,
64349ab3610fSBrian King 	.show_host_maxframe_size = 1,
6435072b91f9SBrian King 
6436072b91f9SBrian King 	.get_host_port_state = ibmvfc_get_host_port_state,
6437072b91f9SBrian King 	.show_host_port_state = 1,
6438072b91f9SBrian King 
6439072b91f9SBrian King 	.get_host_speed = ibmvfc_get_host_speed,
6440072b91f9SBrian King 	.show_host_speed = 1,
6441072b91f9SBrian King 
6442072b91f9SBrian King 	.issue_fc_host_lip = ibmvfc_issue_fc_host_lip,
6443072b91f9SBrian King 	.terminate_rport_io = ibmvfc_terminate_rport_io,
6444072b91f9SBrian King 
6445072b91f9SBrian King 	.show_rport_maxframe_size = 1,
6446072b91f9SBrian King 	.show_rport_supported_classes = 1,
6447072b91f9SBrian King 
6448072b91f9SBrian King 	.set_rport_dev_loss_tmo = ibmvfc_set_rport_dev_loss_tmo,
6449072b91f9SBrian King 	.show_rport_dev_loss_tmo = 1,
6450072b91f9SBrian King 
6451072b91f9SBrian King 	.get_starget_node_name = ibmvfc_get_starget_node_name,
6452072b91f9SBrian King 	.show_starget_node_name = 1,
6453072b91f9SBrian King 
6454072b91f9SBrian King 	.get_starget_port_name = ibmvfc_get_starget_port_name,
6455072b91f9SBrian King 	.show_starget_port_name = 1,
6456072b91f9SBrian King 
6457072b91f9SBrian King 	.get_starget_port_id = ibmvfc_get_starget_port_id,
6458072b91f9SBrian King 	.show_starget_port_id = 1,
6459d31429e1SBrian King 
6460d31429e1SBrian King 	.bsg_request = ibmvfc_bsg_request,
6461d31429e1SBrian King 	.bsg_timeout = ibmvfc_bsg_timeout,
6462072b91f9SBrian King };
6463072b91f9SBrian King 
6464072b91f9SBrian King /**
6465072b91f9SBrian King  * ibmvfc_module_init - Initialize the ibmvfc module
6466072b91f9SBrian King  *
6467072b91f9SBrian King  * Return value:
6468072b91f9SBrian King  * 	0 on success / other on failure
6469072b91f9SBrian King  **/
ibmvfc_module_init(void)6470072b91f9SBrian King static int __init ibmvfc_module_init(void)
6471072b91f9SBrian King {
6472072b91f9SBrian King 	int rc;
6473072b91f9SBrian King 
6474072b91f9SBrian King 	if (!firmware_has_feature(FW_FEATURE_VIO))
6475072b91f9SBrian King 		return -ENODEV;
6476072b91f9SBrian King 
6477072b91f9SBrian King 	printk(KERN_INFO IBMVFC_NAME": IBM Virtual Fibre Channel Driver version: %s %s\n",
6478072b91f9SBrian King 	       IBMVFC_DRIVER_VERSION, IBMVFC_DRIVER_DATE);
6479072b91f9SBrian King 
6480072b91f9SBrian King 	ibmvfc_transport_template = fc_attach_transport(&ibmvfc_transport_functions);
6481072b91f9SBrian King 	if (!ibmvfc_transport_template)
6482072b91f9SBrian King 		return -ENOMEM;
6483072b91f9SBrian King 
6484072b91f9SBrian King 	rc = vio_register_driver(&ibmvfc_driver);
6485072b91f9SBrian King 	if (rc)
6486072b91f9SBrian King 		fc_release_transport(ibmvfc_transport_template);
6487072b91f9SBrian King 	return rc;
6488072b91f9SBrian King }
6489072b91f9SBrian King 
6490072b91f9SBrian King /**
6491072b91f9SBrian King  * ibmvfc_module_exit - Teardown the ibmvfc module
6492072b91f9SBrian King  *
6493072b91f9SBrian King  * Return value:
6494072b91f9SBrian King  * 	nothing
6495072b91f9SBrian King  **/
ibmvfc_module_exit(void)6496072b91f9SBrian King static void __exit ibmvfc_module_exit(void)
6497072b91f9SBrian King {
6498072b91f9SBrian King 	vio_unregister_driver(&ibmvfc_driver);
6499072b91f9SBrian King 	fc_release_transport(ibmvfc_transport_template);
6500072b91f9SBrian King }
6501072b91f9SBrian King 
6502072b91f9SBrian King module_init(ibmvfc_module_init);
6503072b91f9SBrian King module_exit(ibmvfc_module_exit);
6504