ibmvfc.c revision 64b840dd88eb2054f86c72ed6d989cb8681f0058
1c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/*
2c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc.c -- driver for IBM Power Virtual Fibre Channel Adapter
3c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
4c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
5c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
6c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Copyright (C) IBM Corporation, 2008
7c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
8c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * This program is free software; you can redistribute it and/or modify
9c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * it under the terms of the GNU General Public License as published by
10c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * the Free Software Foundation; either version 2 of the License, or
11c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * (at your option) any later version.
12c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
13c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * This program is distributed in the hope that it will be useful,
14c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * but WITHOUT ANY WARRANTY; without even the implied warranty of
15c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * GNU General Public License for more details.
17c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
18c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * You should have received a copy of the GNU General Public License
19c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * along with this program; if not, write to the Free Software
20c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
22c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom */
23c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
24c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <linux/module.h>
25c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <linux/moduleparam.h>
26c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <linux/dma-mapping.h>
27c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <linux/dmapool.h>
28c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <linux/delay.h>
29c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <linux/interrupt.h>
30c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <linux/kthread.h>
31c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <linux/of.h>
32c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <linux/stringify.h>
33c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <asm/firmware.h>
34c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <asm/irq.h>
35c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <asm/vio.h>
36c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <scsi/scsi.h>
37c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <scsi/scsi_cmnd.h>
38c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <scsi/scsi_host.h>
39c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <scsi/scsi_device.h>
40c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <scsi/scsi_tcq.h>
41c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include <scsi/scsi_transport_fc.h>
42c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#include "ibmvfc.h"
43c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
44c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic unsigned int init_timeout = IBMVFC_INIT_TIMEOUT;
45c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT;
46c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic unsigned int max_lun = IBMVFC_MAX_LUN;
47c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic unsigned int max_targets = IBMVFC_MAX_TARGETS;
48c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT;
49c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS;
50c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic unsigned int dev_loss_tmo = IBMVFC_DEV_LOSS_TMO;
51c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic unsigned int ibmvfc_debug = IBMVFC_DEBUG;
52c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL;
53c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic LIST_HEAD(ibmvfc_head);
54c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic DEFINE_SPINLOCK(ibmvfc_driver_lock);
55c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic struct scsi_transport_template *ibmvfc_transport_template;
56c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
57c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_DESCRIPTION("IBM Virtual Fibre Channel Driver");
58c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_AUTHOR("Brian King <brking@linux.vnet.ibm.com>");
59c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_LICENSE("GPL");
60c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_VERSION(IBMVFC_DRIVER_VERSION);
61c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
62c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedommodule_param_named(init_timeout, init_timeout, uint, S_IRUGO | S_IWUSR);
63c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds. "
64c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 "[Default=" __stringify(IBMVFC_INIT_TIMEOUT) "]");
65c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedommodule_param_named(default_timeout, default_timeout, uint, S_IRUGO | S_IWUSR);
66c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_PARM_DESC(default_timeout,
67c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 "Default timeout in seconds for initialization and EH commands. "
68c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 "[Default=" __stringify(IBMVFC_DEFAULT_TIMEOUT) "]");
69c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedommodule_param_named(max_requests, max_requests, uint, S_IRUGO);
70c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_PARM_DESC(max_requests, "Maximum requests for this adapter. "
71c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 "[Default=" __stringify(IBMVFC_MAX_REQUESTS_DEFAULT) "]");
72c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedommodule_param_named(max_lun, max_lun, uint, S_IRUGO);
73c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_PARM_DESC(max_lun, "Maximum allowed LUN. "
74c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 "[Default=" __stringify(IBMVFC_MAX_LUN) "]");
75c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedommodule_param_named(max_targets, max_targets, uint, S_IRUGO);
76c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_PARM_DESC(max_targets, "Maximum allowed targets. "
77c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 "[Default=" __stringify(IBMVFC_MAX_TARGETS) "]");
78c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedommodule_param_named(disc_threads, disc_threads, uint, S_IRUGO | S_IWUSR);
79c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_PARM_DESC(disc_threads, "Number of device discovery threads to use. "
80c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 "[Default=" __stringify(IBMVFC_MAX_DISC_THREADS) "]");
81c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedommodule_param_named(debug, ibmvfc_debug, uint, S_IRUGO | S_IWUSR);
82c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_PARM_DESC(debug, "Enable driver debug information. "
83c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 "[Default=" __stringify(IBMVFC_DEBUG) "]");
84c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedommodule_param_named(dev_loss_tmo, dev_loss_tmo, uint, S_IRUGO | S_IWUSR);
85c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_PARM_DESC(dev_loss_tmo, "Maximum number of seconds that the FC "
86c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 "transport should insulate the loss of a remote port. Once this "
87c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 "value is exceeded, the scsi target is removed. "
88c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 "[Default=" __stringify(IBMVFC_DEV_LOSS_TMO) "]");
89c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedommodule_param_named(log_level, log_level, uint, 0);
90c6e0d91464da214081af546496dd3a4b6d19db70Casey LeedomMODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver. "
91c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 "[Default=" __stringify(IBMVFC_DEFAULT_LOG_LEVEL) "]");
92c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
93c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const struct {
94c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	u16 status;
95c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	u16 error;
96c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	u8 result;
97c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	u8 retry;
98c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int log;
99c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	char *name;
100c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom} cmd_status [] = {
101c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_UNABLE_TO_ESTABLISH, DID_ERROR, 1, 1, "unable to establish" },
102c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_FAULT, DID_OK, 1, 0, "transport fault" },
103c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_CMD_TIMEOUT, DID_TIME_OUT, 1, 1, "command timeout" },
104c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_ENETDOWN, DID_TRANSPORT_DISRUPTED, 1, 1, "network down" },
105c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_HW_FAILURE, DID_ERROR, 1, 1, "hardware failure" },
106c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_DOWN_ERR, DID_REQUEUE, 0, 0, "link down" },
107c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_DEAD_ERR, DID_ERROR, 0, 0, "link dead" },
108c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_UNABLE_TO_REGISTER, DID_ERROR, 1, 1, "unable to register" },
109c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_BUSY, DID_BUS_BUSY, 1, 0, "transport busy" },
110c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_DEAD, DID_ERROR, 0, 1, "transport dead" },
111c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_CONFIG_ERROR, DID_ERROR, 1, 1, "configuration error" },
112c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_NAME_SERVER_FAIL, DID_ERROR, 1, 1, "name server failure" },
113c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 0, 0, "link halted" },
114c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_GENERAL, DID_OK, 1, 0, "general transport error" },
115c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
116c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_VIOS_FAILURE, IBMVFC_CRQ_FAILURE, DID_REQUEUE, 1, 1, "CRQ failure" },
117c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_VIOS_FAILURE, IBMVFC_SW_FAILURE, DID_ERROR, 0, 1, "software failure" },
118c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_VIOS_FAILURE, IBMVFC_INVALID_PARAMETER, DID_ERROR, 0, 1, "invalid parameter" },
119c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_VIOS_FAILURE, IBMVFC_MISSING_PARAMETER, DID_ERROR, 0, 1, "missing parameter" },
120c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_VIOS_FAILURE, IBMVFC_HOST_IO_BUS, DID_ERROR, 1, 1, "host I/O bus failure" },
121c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED, DID_ERROR, 0, 1, "transaction cancelled" },
122c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED_IMPLICIT, DID_ERROR, 0, 1, "transaction cancelled implicit" },
123c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_VIOS_FAILURE, IBMVFC_INSUFFICIENT_RESOURCE, DID_REQUEUE, 1, 1, "insufficient resources" },
124c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_VIOS_FAILURE, IBMVFC_PLOGI_REQUIRED, DID_ERROR, 0, 1, "port login required" },
125c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_VIOS_FAILURE, IBMVFC_COMMAND_FAILED, DID_ERROR, 1, 1, "command failed" },
126c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
127c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FC_FAILURE, IBMVFC_INVALID_ELS_CMD_CODE, DID_ERROR, 0, 1, "invalid ELS command code" },
128c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FC_FAILURE, IBMVFC_INVALID_VERSION, DID_ERROR, 0, 1, "invalid version level" },
129c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FC_FAILURE, IBMVFC_LOGICAL_ERROR, DID_ERROR, 1, 1, "logical error" },
130c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FC_FAILURE, IBMVFC_INVALID_CT_IU_SIZE, DID_ERROR, 0, 1, "invalid CT_IU size" },
131c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FC_FAILURE, IBMVFC_LOGICAL_BUSY, DID_REQUEUE, 1, 0, "logical busy" },
132c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FC_FAILURE, IBMVFC_PROTOCOL_ERROR, DID_ERROR, 1, 1, "protocol error" },
133c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FC_FAILURE, IBMVFC_UNABLE_TO_PERFORM_REQ, DID_ERROR, 1, 1, "unable to perform request" },
134c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FC_FAILURE, IBMVFC_CMD_NOT_SUPPORTED, DID_ERROR, 0, 0, "command not supported" },
135c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FC_FAILURE, IBMVFC_SERVER_NOT_AVAIL, DID_ERROR, 0, 1, "server not available" },
136c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FC_FAILURE, IBMVFC_CMD_IN_PROGRESS, DID_ERROR, 0, 1, "command already in progress" },
137c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FC_FAILURE, IBMVFC_VENDOR_SPECIFIC, DID_ERROR, 1, 1, "vendor specific" },
138c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
139c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FC_SCSI_ERROR, 0, DID_OK, 1, 0, "SCSI error" },
140c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom};
141c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
142c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_npiv_login(struct ibmvfc_host *);
143c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_tgt_send_prli(struct ibmvfc_target *);
144c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_tgt_send_plogi(struct ibmvfc_target *);
145c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_tgt_query_target(struct ibmvfc_target *);
146c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
147c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const char *unknown_error = "unknown error";
148c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
149c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#ifdef CONFIG_SCSI_IBMVFC_TRACE
150c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
151c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_trc_start - Log a start trace entry
152c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:		ibmvfc event struct
153c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
154c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
155c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_trc_start(struct ibmvfc_event *evt)
156c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
157c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = evt->vhost;
158c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd;
159c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_mad_common *mad = &evt->iu.mad_common;
160c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_trace_entry *entry;
161c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
162c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	entry = &vhost->trace[vhost->trace_index++];
163c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	entry->evt = evt;
164c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	entry->time = jiffies;
165c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	entry->fmt = evt->crq.format;
166c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	entry->type = IBMVFC_TRC_START;
167c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
168c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	switch (entry->fmt) {
169c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_CMD_FORMAT:
170c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->op_code = vfc_cmd->iu.cdb[0];
171c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->scsi_id = vfc_cmd->tgt_scsi_id;
172c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->lun = scsilun_to_int(&vfc_cmd->iu.lun);
173c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->tmf_flags = vfc_cmd->iu.tmf_flags;
174c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->u.start.xfer_len = vfc_cmd->iu.xfer_len;
175c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
176c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_MAD_FORMAT:
177c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->op_code = mad->opcode;
178c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
179c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	default:
180c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
181c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	};
182c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
183c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
184c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
185c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_trc_end - Log an end trace entry
186c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:		ibmvfc event struct
187c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
188c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
189c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_trc_end(struct ibmvfc_event *evt)
190c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
191c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = evt->vhost;
192c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
193c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_mad_common *mad = &evt->xfer_iu->mad_common;
194c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_trace_entry *entry = &vhost->trace[vhost->trace_index++];
195c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
196c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	entry->evt = evt;
197c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	entry->time = jiffies;
198c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	entry->fmt = evt->crq.format;
199c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	entry->type = IBMVFC_TRC_END;
200c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
201c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	switch (entry->fmt) {
202c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_CMD_FORMAT:
203c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->op_code = vfc_cmd->iu.cdb[0];
204c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->scsi_id = vfc_cmd->tgt_scsi_id;
205c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->lun = scsilun_to_int(&vfc_cmd->iu.lun);
206c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->tmf_flags = vfc_cmd->iu.tmf_flags;
207c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->u.end.status = vfc_cmd->status;
208c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->u.end.error = vfc_cmd->error;
209c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->u.end.fcp_rsp_flags = vfc_cmd->rsp.flags;
210c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->u.end.rsp_code = vfc_cmd->rsp.data.info.rsp_code;
211c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->u.end.scsi_status = vfc_cmd->rsp.scsi_status;
212c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
213c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_MAD_FORMAT:
214c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->op_code = mad->opcode;
215c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		entry->u.end.status = mad->status;
216c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
217c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	default:
218c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
219c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
220c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	};
221c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
222c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
223c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#else
224c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#define ibmvfc_trc_start(evt) do { } while (0)
225c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#define ibmvfc_trc_end(evt) do { } while (0)
226c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom#endif
227c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
228c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
229c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_err_index - Find the index into cmd_status for the fcp response
230c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @status:		status / error class
23157b2eaf7ddeae307fac202d82a6fabf5976e575bFUJITA Tomonori * @error:		error
23257b2eaf7ddeae307fac202d82a6fabf5976e575bFUJITA Tomonori *
23357b2eaf7ddeae307fac202d82a6fabf5976e575bFUJITA Tomonori * Return value:
23457b2eaf7ddeae307fac202d82a6fabf5976e575bFUJITA Tomonori *	index into cmd_status / -EINVAL on failure
23557b2eaf7ddeae307fac202d82a6fabf5976e575bFUJITA Tomonori **/
236c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_get_err_index(u16 status, u16 error)
237c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
238c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int i;
239c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
240c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	for (i = 0; i < ARRAY_SIZE(cmd_status); i++)
241c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if ((cmd_status[i].status & status) == cmd_status[i].status &&
242c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		    cmd_status[i].error == error)
243c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			return i;
244c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
245c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return -EINVAL;
246c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
247c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
248c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
249c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_cmd_error - Find the error description for the fcp response
250c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @status:		status / error class
251c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @error:		error
252c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
253c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
254c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	error description string
255c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
256c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const char *ibmvfc_get_cmd_error(u16 status, u16 error)
257c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
258c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc = ibmvfc_get_err_index(status, error);
259c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (rc >= 0)
260c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return cmd_status[rc].name;
261c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return unknown_error;
262c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
263c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
264c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
265c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_err_result - Find the scsi status to return for the fcp response
266c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vfc_cmd:	ibmvfc command struct
267c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
268c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
269c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	SCSI result value to return for completed command
270c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
271c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_get_err_result(struct ibmvfc_cmd *vfc_cmd)
272c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
273c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int err;
274c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
275c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int fc_rsp_len = rsp->fcp_rsp_len;
276c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
277c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if ((rsp->flags & FCP_RSP_LEN_VALID) &&
278c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	    ((!fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) ||
279c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	     rsp->data.info.rsp_code))
280c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return DID_ERROR << 16;
281c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
282c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	err = ibmvfc_get_err_index(vfc_cmd->status, vfc_cmd->error);
283c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (err >= 0)
284c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return rsp->scsi_status | (cmd_status[err].result << 16);
285c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return rsp->scsi_status | (DID_ERROR << 16);
286c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
287c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
288c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
289c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_retry_cmd - Determine if error status is retryable
290c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @status:		status / error class
291c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @error:		error
292c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
293c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
294c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	1 if error should be retried / 0 if it should not
295c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
296c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_retry_cmd(u16 status, u16 error)
297c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
298c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc = ibmvfc_get_err_index(status, error);
299c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
300c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (rc >= 0)
301c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return cmd_status[rc].retry;
302c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return 1;
303c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
304c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
305c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const char *unknown_fc_explain = "unknown fc explain";
306c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
307c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const struct {
308c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	u16 fc_explain;
309c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	char *name;
310c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom} ls_explain [] = {
311c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x00, "no additional explanation" },
312c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x01, "service parameter error - options" },
313c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x03, "service parameter error - initiator control" },
314c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x05, "service parameter error - recipient control" },
315c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x07, "service parameter error - received data field size" },
316c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x09, "service parameter error - concurrent seq" },
317c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x0B, "service parameter error - credit" },
318c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x0D, "invalid N_Port/F_Port_Name" },
319c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x0E, "invalid node/Fabric Name" },
320c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x0F, "invalid common service parameters" },
321c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x11, "invalid association header" },
322c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x13, "association header required" },
323c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x15, "invalid originator S_ID" },
324c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x17, "invalid OX_ID-RX-ID combination" },
325c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x19, "command (request) already in progress" },
326c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x1E, "N_Port Login requested" },
327c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x1F, "Invalid N_Port_ID" },
328c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom};
329c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
330c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const struct {
331c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	u16 fc_explain;
332c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	char *name;
333c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom} gs_explain [] = {
334c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x00, "no additional explanation" },
335c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x01, "port identifier not registered" },
336c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x02, "port name not registered" },
337c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x03, "node name not registered" },
338c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x04, "class of service not registered" },
339c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x06, "initial process associator not registered" },
340c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x07, "FC-4 TYPEs not registered" },
341c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x08, "symbolic port name not registered" },
342c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x09, "symbolic node name not registered" },
343c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0x0A, "port type not registered" },
344c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0xF0, "authorization exception" },
345c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0xF1, "authentication exception" },
346c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0xF2, "data base full" },
347c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0xF3, "data base empty" },
348c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0xF4, "processing request" },
349c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0xF5, "unable to verify connection" },
350c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ 0xF6, "devices not in a common zone" },
351c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom};
352c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
353c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
354c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_ls_explain - Return the FC Explain description text
355c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @status:	FC Explain status
356c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
357c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
358c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	error string
359c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
360c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const char *ibmvfc_get_ls_explain(u16 status)
361c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
362c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int i;
363c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
364c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	for (i = 0; i < ARRAY_SIZE(ls_explain); i++)
365c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (ls_explain[i].fc_explain == status)
366c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			return ls_explain[i].name;
367c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
368c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return unknown_fc_explain;
369c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
370c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
371c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
372c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_gs_explain - Return the FC Explain description text
373c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @status:	FC Explain status
374c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
375c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
376c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	error string
377c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
378c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const char *ibmvfc_get_gs_explain(u16 status)
379c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
380c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int i;
381c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
382c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	for (i = 0; i < ARRAY_SIZE(gs_explain); i++)
383c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (gs_explain[i].fc_explain == status)
384c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			return gs_explain[i].name;
385c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
386c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return unknown_fc_explain;
387c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
388c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
389c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const struct {
390c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	enum ibmvfc_fc_type fc_type;
391c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	char *name;
392c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom} fc_type [] = {
393c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_REJECT, "fabric reject" },
394c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_PORT_REJECT, "port reject" },
395c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_LS_REJECT, "ELS reject" },
396c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_FABRIC_BUSY, "fabric busy" },
397c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_PORT_BUSY, "port busy" },
398c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_BASIC_REJECT, "basic reject" },
399c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom};
400c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
401c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const char *unknown_fc_type = "unknown fc type";
402c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
403c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
404c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_fc_type - Return the FC Type description text
405c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @status:	FC Type error status
406c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
407c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
408c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	error string
409c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
410c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const char *ibmvfc_get_fc_type(u16 status)
411c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
412c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int i;
413c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
414c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	for (i = 0; i < ARRAY_SIZE(fc_type); i++)
415c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (fc_type[i].fc_type == status)
416c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			return fc_type[i].name;
417c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
418c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return unknown_fc_type;
419c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
420c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
421c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
422c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_set_tgt_action - Set the next init action for the target
423c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @tgt:		ibmvfc target struct
424c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @action:		action to perform
425c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
426c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
427c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_set_tgt_action(struct ibmvfc_target *tgt,
428c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				  enum ibmvfc_target_action action)
429c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
430c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	switch (tgt->action) {
431c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_TGT_ACTION_DEL_RPORT:
432c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
433c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	default:
434c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tgt->action = action;
435c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
436c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
437c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
438c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
439c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
440c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_set_host_state - Set the state for the host
441c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:		ibmvfc host struct
442c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @state:		state to set host to
443c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
444c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
445c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 if state changed / non-zero if not changed
446c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
447c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_set_host_state(struct ibmvfc_host *vhost,
448c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				  enum ibmvfc_host_state state)
449c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
450c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc = 0;
451c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
452c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	switch (vhost->state) {
453c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_OFFLINE:
454c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rc = -EINVAL;
455c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
456c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	default:
457c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vhost->state = state;
458c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
459c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	};
460c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
461c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return rc;
462c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
463c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
464c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
465c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_set_host_action - Set the next init action for the host
466c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:		ibmvfc host struct
467c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @action:		action to perform
468c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
469c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
470c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
471c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				   enum ibmvfc_host_action action)
472c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
473c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	switch (action) {
474c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_ACTION_ALLOC_TGTS:
475c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT)
476c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			vhost->action = action;
477c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
478c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_ACTION_INIT_WAIT:
479c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (vhost->action == IBMVFC_HOST_ACTION_INIT)
480c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			vhost->action = action;
481c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
482c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_ACTION_QUERY:
483c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		switch (vhost->action) {
484c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case IBMVFC_HOST_ACTION_INIT_WAIT:
485c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case IBMVFC_HOST_ACTION_NONE:
486c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case IBMVFC_HOST_ACTION_TGT_ADD:
487c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			vhost->action = action;
488c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
489c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		default:
490c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
491c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		};
492c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
493c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_ACTION_TGT_INIT:
494c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS)
495c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			vhost->action = action;
496c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
497c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_ACTION_INIT:
498c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_ACTION_TGT_DEL:
499c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_ACTION_QUERY_TGTS:
500c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
501c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_ACTION_TGT_ADD:
502c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_ACTION_NONE:
503c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	default:
504c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vhost->action = action;
505c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
506c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	};
507c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
508c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
509c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
510c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_reinit_host - Re-start host initialization (no NPIV Login)
511c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:		ibmvfc host struct
512c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
513c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
514c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	nothing
515c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
516c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_reinit_host(struct ibmvfc_host *vhost)
517c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
518c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (vhost->action == IBMVFC_HOST_ACTION_NONE) {
519c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
520c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			scsi_block_requests(vhost->host);
521c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
522c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
523c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} else
524c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vhost->reinit = 1;
525c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
526c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	wake_up(&vhost->work_wait_q);
527c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
528c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
529c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
530c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_link_down - Handle a link down event from the adapter
531c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
532c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @state:	ibmvfc host state to enter
533c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
534c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
535c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_link_down(struct ibmvfc_host *vhost,
536c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			     enum ibmvfc_host_state state)
537c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
538c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_target *tgt;
539c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
540c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ENTER;
541c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	scsi_block_requests(vhost->host);
542c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	list_for_each_entry(tgt, &vhost->targets, queue)
543c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
544c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_set_host_state(vhost, state);
545c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
546c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vhost->events_to_log |= IBMVFC_AE_LINKDOWN;
547c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	wake_up(&vhost->work_wait_q);
548c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	LEAVE;
549c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
550c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
551c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
552c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_init_host - Start host initialization
553c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:		ibmvfc host struct
554c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @relogin:	is this a re-login?
555c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
556c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
557c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	nothing
558c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
559c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
560c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
561c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_target *tgt;
562c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
563c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
564c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
565c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			dev_err(vhost->dev,
566c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				"Host initialization retries exceeded. Taking adapter offline\n");
567c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
568c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			return;
569c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
570c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
571c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
572c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
573c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (!relogin) {
574c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
575c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			vhost->async_crq.cur = 0;
576c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
577c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
578c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		list_for_each_entry(tgt, &vhost->targets, queue)
579c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			tgt->need_login = 1;
580c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		scsi_block_requests(vhost->host);
581c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
582c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vhost->job_step = ibmvfc_npiv_login;
583c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		wake_up(&vhost->work_wait_q);
584c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
585c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
586c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
587c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
588c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_send_crq - Send a CRQ
589c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
590c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @word1:	the first 64 bits of the data
591c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @word2:	the second 64 bits of the data
592c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
593c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
594c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 on success / other on failure
595c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
596c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_send_crq(struct ibmvfc_host *vhost, u64 word1, u64 word2)
597c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
598c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct vio_dev *vdev = to_vio_dev(vhost->dev);
599c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2);
600c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
601c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
602c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
603c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_send_crq_init - Send a CRQ init message
604c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
605c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
606c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
607c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 on success / other on failure
608c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
609c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_send_crq_init(struct ibmvfc_host *vhost)
610c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
611c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_dbg(vhost, "Sending CRQ init\n");
612c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return ibmvfc_send_crq(vhost, 0xC001000000000000LL, 0);
613c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
614c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
615c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
616c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_send_crq_init_complete - Send a CRQ init complete message
617c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
618c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
619c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
620c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 on success / other on failure
621c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
622c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_send_crq_init_complete(struct ibmvfc_host *vhost)
623c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
624c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_dbg(vhost, "Sending CRQ init complete\n");
625c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return ibmvfc_send_crq(vhost, 0xC002000000000000LL, 0);
626c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
627c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
628c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
629c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_release_crq_queue - Deallocates data and unregisters CRQ
630c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
631c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
632c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Frees irq, deallocates a page for messages, unmaps dma, and unregisters
633c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * the crq with the hypervisor.
634c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
635c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
636c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
637c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	long rc;
638c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct vio_dev *vdev = to_vio_dev(vhost->dev);
639c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_crq_queue *crq = &vhost->crq;
640c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
641c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_dbg(vhost, "Releasing CRQ\n");
642c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	free_irq(vdev->irq, vhost);
643c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	do {
644c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
645c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
646c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
647c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vhost->state = IBMVFC_NO_CRQ;
648c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	dma_unmap_single(vhost->dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
649c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	free_page((unsigned long)crq->msgs);
650c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
651c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
652c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
653c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_reenable_crq_queue - reenables the CRQ
654c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
655c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
656c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
657c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 on success / other on failure
658c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
659c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
660c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
661c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc;
662c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct vio_dev *vdev = to_vio_dev(vhost->dev);
663c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
664c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	/* Re-enable the CRQ */
665c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	do {
666c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address);
667c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
668c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
669c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (rc)
670c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		dev_err(vhost->dev, "Error enabling adapter (rc=%d)\n", rc);
671c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
672c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return rc;
673c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
674c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
675c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
676c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_reset_crq - resets a crq after a failure
677c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
678c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
679c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
680c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 on success / other on failure
681c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
682c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
683c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
684c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc;
685c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct vio_dev *vdev = to_vio_dev(vhost->dev);
686c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_crq_queue *crq = &vhost->crq;
687c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
688c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	/* Close the CRQ */
689c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	do {
690c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
691c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
692c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
693c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vhost->state = IBMVFC_NO_CRQ;
694c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
695c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
696c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	/* Clean out the queue */
697c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	memset(crq->msgs, 0, PAGE_SIZE);
698c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	crq->cur = 0;
699c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
700c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	/* And re-open it again */
701c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
702c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				crq->msg_token, PAGE_SIZE);
703c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
704c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (rc == H_CLOSED)
705c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		/* Adapter is good, but other end is not ready */
706c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		dev_warn(vhost->dev, "Partner adapter not ready\n");
707c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	else if (rc != 0)
708c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc);
709c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
710c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return rc;
711c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
712c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
713c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
714c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_valid_event - Determines if event is valid.
715c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @pool:	event_pool that contains the event
716c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:	ibmvfc event to be checked for validity
717c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
718c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
719c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	1 if event is valid / 0 if event is not valid
720c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
721c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_valid_event(struct ibmvfc_event_pool *pool,
722c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			      struct ibmvfc_event *evt)
723c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
724c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int index = evt - pool->events;
725c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (index < 0 || index >= pool->size)	/* outside of bounds */
726c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return 0;
727c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (evt != pool->events + index)	/* unaligned */
728c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return 0;
729c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return 1;
730c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
731c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
732c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
733c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_free_event - Free the specified event
734c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:	ibmvfc_event to be freed
735c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
736c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
737c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_free_event(struct ibmvfc_event *evt)
738c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
739c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = evt->vhost;
740c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_event_pool *pool = &vhost->pool;
741c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
742c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	BUG_ON(!ibmvfc_valid_event(pool, evt));
743c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	BUG_ON(atomic_inc_return(&evt->free) != 1);
744c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	list_add_tail(&evt->queue, &vhost->free);
745c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
746c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
747c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
748c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_scsi_eh_done - EH done function for queuecommand commands
749c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:	ibmvfc event struct
750c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
751c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * This function does not setup any error status, that must be done
752c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * before this function gets called.
753c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
754c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
755c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
756c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct scsi_cmnd *cmnd = evt->cmnd;
757c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
758c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (cmnd) {
759c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		scsi_dma_unmap(cmnd);
760c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		cmnd->scsi_done(cmnd);
761c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
762c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
763c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (evt->eh_comp)
764c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		complete(evt->eh_comp);
765c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
766c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_free_event(evt);
767c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
768c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
769c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
770c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_fail_request - Fail request with specified error code
771c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:		ibmvfc event struct
772c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @error_code:	error code to fail request with
773c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
774c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
775c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	none
776c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
777c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
778c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
779c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (evt->cmnd) {
780c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->cmnd->result = (error_code << 16);
781c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->done = ibmvfc_scsi_eh_done;
782c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} else
783c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->xfer_iu->mad_common.status = IBMVFC_MAD_DRIVER_FAILED;
784c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
785c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	list_del(&evt->queue);
786c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	del_timer(&evt->timer);
787c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_trc_end(evt);
788c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	evt->done(evt);
789c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
790c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
791c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
792c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_purge_requests - Our virtual adapter just shut down. Purge any sent requests
793c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:		ibmvfc host struct
794c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @error_code:	error code to fail requests with
795c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
796c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
797c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	none
798c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
799c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
800c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
801c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_event *evt, *pos;
802c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
803c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_dbg(vhost, "Purging all requests\n");
804c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	list_for_each_entry_safe(evt, pos, &vhost->sent, queue)
805c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_fail_request(evt, error_code);
806c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
807c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
808c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
809c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * __ibmvfc_reset_host - Reset the connection to the server (no locking)
810c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	struct ibmvfc host to reset
811c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
812c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void __ibmvfc_reset_host(struct ibmvfc_host *vhost)
813c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
814c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc;
815c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
816c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	scsi_block_requests(vhost->host);
817c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_purge_requests(vhost, DID_ERROR);
818c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if ((rc = ibmvfc_reset_crq(vhost)) ||
819c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	    (rc = ibmvfc_send_crq_init(vhost)) ||
820c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	    (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) {
821c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		dev_err(vhost->dev, "Error after reset rc=%d\n", rc);
822c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
823c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} else
824c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
825c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
826c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
827c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
828c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_reset_host - Reset the connection to the server
829c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	struct ibmvfc host to reset
830c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
831c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_reset_host(struct ibmvfc_host *vhost)
832c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
833c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long flags;
834c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
835c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(vhost->host->host_lock, flags);
836c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	__ibmvfc_reset_host(vhost);
837c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(vhost->host->host_lock, flags);
838c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
839c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
840c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
841c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_retry_host_init - Retry host initialization if allowed
842c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
843c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
844c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
845c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
846c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
847c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
848c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vhost->delay_init = 1;
849c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (++vhost->init_retries > IBMVFC_MAX_HOST_INIT_RETRIES) {
850c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			dev_err(vhost->dev,
851c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				"Host initialization retries exceeded. Taking adapter offline\n");
852c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
853c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		} else if (vhost->init_retries == IBMVFC_MAX_HOST_INIT_RETRIES)
854c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			__ibmvfc_reset_host(vhost);
855c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		else
856c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
857c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
858c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
859c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	wake_up(&vhost->work_wait_q);
860c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
861c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
862c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
863c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * __ibmvfc_get_target - Find the specified scsi_target (no locking)
864c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @starget:	scsi target struct
865c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
866c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
867c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	ibmvfc_target struct / NULL if not found
868c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
869c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic struct ibmvfc_target *__ibmvfc_get_target(struct scsi_target *starget)
870c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
871c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
872c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(shost);
873c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_target *tgt;
874c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
875c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	list_for_each_entry(tgt, &vhost->targets, queue)
876c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (tgt->target_id == starget->id) {
877c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			kref_get(&tgt->kref);
878c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			return tgt;
879c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
880c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return NULL;
881c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
882c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
883c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
884c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_target - Find the specified scsi_target
885c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @starget:	scsi target struct
886c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
887c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
888c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	ibmvfc_target struct / NULL if not found
889c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
890c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic struct ibmvfc_target *ibmvfc_get_target(struct scsi_target *starget)
891c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
892c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
893c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_target *tgt;
894c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long flags;
895c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
896c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(shost->host_lock, flags);
897c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	tgt = __ibmvfc_get_target(starget);
898c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(shost->host_lock, flags);
899c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return tgt;
900c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
901c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
902c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
903c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_host_speed - Get host port speed
904c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @shost:		scsi host struct
905c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
906c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
907c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * 	none
908c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
909c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_get_host_speed(struct Scsi_Host *shost)
910c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
911c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(shost);
912c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long flags;
913c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
914c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(shost->host_lock, flags);
915c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (vhost->state == IBMVFC_ACTIVE) {
916c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		switch (vhost->login_buf->resp.link_speed / 100) {
917c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case 1:
918c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
919c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
920c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case 2:
921c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			fc_host_speed(shost) = FC_PORTSPEED_2GBIT;
922c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
923c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case 4:
924c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			fc_host_speed(shost) = FC_PORTSPEED_4GBIT;
925c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
926c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case 8:
927c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			fc_host_speed(shost) = FC_PORTSPEED_8GBIT;
928c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
929c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case 10:
930c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
931c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
932c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case 16:
933c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			fc_host_speed(shost) = FC_PORTSPEED_16GBIT;
934c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
935c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		default:
936c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			ibmvfc_log(vhost, 3, "Unknown port speed: %lld Gbit\n",
937c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				   vhost->login_buf->resp.link_speed / 100);
938c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
939c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
940c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
941c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} else
942c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
943c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(shost->host_lock, flags);
944c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
945c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
946c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
947c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_host_port_state - Get host port state
948c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @shost:		scsi host struct
949c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
950c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
951c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * 	none
952c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
953c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_get_host_port_state(struct Scsi_Host *shost)
954c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
955c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(shost);
956c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long flags;
957c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
958c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(shost->host_lock, flags);
959c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	switch (vhost->state) {
960c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_INITIALIZING:
961c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_ACTIVE:
962c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
963c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
964c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_LINK_DOWN:
965c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
966c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
967c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_LINK_DEAD:
968c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_OFFLINE:
969c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
970c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
971c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HALTED:
972c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		fc_host_port_state(shost) = FC_PORTSTATE_BLOCKED;
973c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
974c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_NO_CRQ:
975c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
976c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
977c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	default:
978c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_log(vhost, 3, "Unknown port state: %d\n", vhost->state);
979c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
980c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
981c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
982c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(shost->host_lock, flags);
983c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
984c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
985c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
986c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_set_rport_dev_loss_tmo - Set rport's device loss timeout
987c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @rport:		rport struct
988c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @timeout:	timeout value
989c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
990c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
991c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * 	none
992c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
993c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
994c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
995c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (timeout)
996c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rport->dev_loss_tmo = timeout;
997c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	else
998c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rport->dev_loss_tmo = 1;
999c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1000c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1001c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1002c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_release_tgt - Free memory allocated for a target
1003c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @kref:		kref struct
1004c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1005c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1006c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_release_tgt(struct kref *kref)
1007c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1008c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
1009c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	kfree(tgt);
1010c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1011c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1012c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1013c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_starget_node_name - Get SCSI target's node name
1014c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @starget:	scsi target struct
1015c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1016c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
1017c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * 	none
1018c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1019c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_get_starget_node_name(struct scsi_target *starget)
1020c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1021c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
1022c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	fc_starget_port_name(starget) = tgt ? tgt->ids.node_name : 0;
1023c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (tgt)
1024c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		kref_put(&tgt->kref, ibmvfc_release_tgt);
1025c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1026c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1027c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1028c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_starget_port_name - Get SCSI target's port name
1029c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @starget:	scsi target struct
1030c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1031c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
1032c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * 	none
1033c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1034c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_get_starget_port_name(struct scsi_target *starget)
1035c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1036c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
1037c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	fc_starget_port_name(starget) = tgt ? tgt->ids.port_name : 0;
1038c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (tgt)
1039c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		kref_put(&tgt->kref, ibmvfc_release_tgt);
1040c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1041c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1042c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1043c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_starget_port_id - Get SCSI target's port ID
1044c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @starget:	scsi target struct
1045c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1046c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
1047c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * 	none
1048c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1049c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_get_starget_port_id(struct scsi_target *starget)
1050c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1051c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_target *tgt = ibmvfc_get_target(starget);
1052c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	fc_starget_port_id(starget) = tgt ? tgt->scsi_id : -1;
1053c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (tgt)
1054c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		kref_put(&tgt->kref, ibmvfc_release_tgt);
1055c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1056c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1057c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1058c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_wait_while_resetting - Wait while the host resets
1059c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:		ibmvfc host struct
1060c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1061c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
1062c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * 	0 on success / other on failure
1063c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1064c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_wait_while_resetting(struct ibmvfc_host *vhost)
1065c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1066c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	long timeout = wait_event_timeout(vhost->init_wait_q,
1067c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom					  ((vhost->state == IBMVFC_ACTIVE ||
1068c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom					    vhost->state == IBMVFC_HOST_OFFLINE ||
1069c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom					    vhost->state == IBMVFC_LINK_DEAD) &&
1070c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom					   vhost->action == IBMVFC_HOST_ACTION_NONE),
1071c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom					  (init_timeout * HZ));
1072c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1073c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return timeout ? 0 : -EIO;
1074c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1075c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1076c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1077c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_issue_fc_host_lip - Re-initiate link initialization
1078c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @shost:		scsi host struct
1079c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1080c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
1081c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * 	0 on success / other on failure
1082c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1083c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_issue_fc_host_lip(struct Scsi_Host *shost)
1084c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1085c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(shost);
1086c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1087c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	dev_err(vhost->dev, "Initiating host LIP. Resetting connection\n");
1088c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_reset_host(vhost);
1089c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return ibmvfc_wait_while_resetting(vhost);
1090c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1091c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1092c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1093c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_gather_partition_info - Gather info about the LPAR
1094c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1095c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
1096c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	none
1097c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1098c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_gather_partition_info(struct ibmvfc_host *vhost)
1099c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1100c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct device_node *rootdn;
1101c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	const char *name;
1102c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	const unsigned int *num;
1103c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1104c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	rootdn = of_find_node_by_path("/");
1105c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!rootdn)
1106c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return;
1107c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1108c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	name = of_get_property(rootdn, "ibm,partition-name", NULL);
1109c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (name)
1110c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		strncpy(vhost->partition_name, name, sizeof(vhost->partition_name));
1111c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	num = of_get_property(rootdn, "ibm,partition-no", NULL);
1112c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (num)
1113c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vhost->partition_number = *num;
1114c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	of_node_put(rootdn);
1115c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1116c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1117c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1118c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_set_login_info - Setup info for NPIV login
1119c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
1120c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1121c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
1122c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	none
1123c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1124c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
1125c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1126c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_npiv_login *login_info = &vhost->login_info;
1127c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct device_node *of_node = vhost->dev->archdata.of_node;
1128c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	const char *location;
1129c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1130c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	memset(login_info, 0, sizeof(*login_info));
1131c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1132c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	login_info->ostype = IBMVFC_OS_LINUX;
1133c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	login_info->max_dma_len = IBMVFC_MAX_SECTORS << 9;
1134c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	login_info->max_payload = sizeof(struct ibmvfc_fcp_cmd_iu);
1135c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	login_info->max_response = sizeof(struct ibmvfc_fcp_rsp);
1136c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	login_info->partition_num = vhost->partition_number;
1137c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	login_info->vfc_frame_version = 1;
1138c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	login_info->fcp_version = 3;
1139c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (vhost->client_migrated)
1140c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		login_info->flags = IBMVFC_CLIENT_MIGRATED;
1141c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1142c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	login_info->max_cmds = max_requests + IBMVFC_NUM_INTERNAL_REQ;
1143c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	login_info->capabilities = IBMVFC_CAN_MIGRATE;
1144c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	login_info->async.va = vhost->async_crq.msg_token;
1145c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	login_info->async.len = vhost->async_crq.size * sizeof(*vhost->async_crq.msgs);
1146c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	strncpy(login_info->partition_name, vhost->partition_name, IBMVFC_MAX_NAME);
1147c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	strncpy(login_info->device_name,
1148c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		dev_name(&vhost->host->shost_gendev), IBMVFC_MAX_NAME);
1149c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1150c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	location = of_get_property(of_node, "ibm,loc-code", NULL);
1151c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	location = location ? location : dev_name(vhost->dev);
1152c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	strncpy(login_info->drc_name, location, IBMVFC_MAX_NAME);
1153c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1154c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1155c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1156c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_init_event_pool - Allocates and initializes the event pool for a host
1157c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host who owns the event pool
1158c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1159c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns zero on success.
1160c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1161c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_init_event_pool(struct ibmvfc_host *vhost)
1162c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1163c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int i;
1164c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_event_pool *pool = &vhost->pool;
1165c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1166c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ENTER;
1167c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	pool->size = max_requests + IBMVFC_NUM_INTERNAL_REQ;
1168c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	pool->events = kcalloc(pool->size, sizeof(*pool->events), GFP_KERNEL);
1169c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!pool->events)
1170c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return -ENOMEM;
1171c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1172c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	pool->iu_storage = dma_alloc_coherent(vhost->dev,
1173c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom					      pool->size * sizeof(*pool->iu_storage),
1174c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom					      &pool->iu_token, 0);
1175c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1176c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!pool->iu_storage) {
1177c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		kfree(pool->events);
1178c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return -ENOMEM;
1179c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1180c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1181c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	for (i = 0; i < pool->size; ++i) {
1182c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		struct ibmvfc_event *evt = &pool->events[i];
1183c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		atomic_set(&evt->free, 1);
1184c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->crq.valid = 0x80;
1185c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->crq.ioba = pool->iu_token + (sizeof(*evt->xfer_iu) * i);
1186c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->xfer_iu = pool->iu_storage + i;
1187c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->vhost = vhost;
1188c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->ext_list = NULL;
1189c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		list_add_tail(&evt->queue, &vhost->free);
1190c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1191c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1192c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	LEAVE;
1193c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return 0;
1194c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1195c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1196c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1197c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_free_event_pool - Frees memory of the event pool of a host
1198c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host who owns the event pool
1199c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1200c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1201c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_free_event_pool(struct ibmvfc_host *vhost)
1202c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1203c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int i;
1204c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_event_pool *pool = &vhost->pool;
1205c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1206c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ENTER;
1207c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	for (i = 0; i < pool->size; ++i) {
1208c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		list_del(&pool->events[i].queue);
1209c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		BUG_ON(atomic_read(&pool->events[i].free) != 1);
1210c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (pool->events[i].ext_list)
1211c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			dma_pool_free(vhost->sg_pool,
1212c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				      pool->events[i].ext_list,
1213c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				      pool->events[i].ext_list_token);
1214c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1215c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1216c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	kfree(pool->events);
1217c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	dma_free_coherent(vhost->dev,
1218c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			  pool->size * sizeof(*pool->iu_storage),
1219c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			  pool->iu_storage, pool->iu_token);
1220c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	LEAVE;
1221c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1222c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1223c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1224c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_event - Gets the next free event in pool
1225c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
1226c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1227c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns a free event from the pool.
1228c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1229c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic struct ibmvfc_event *ibmvfc_get_event(struct ibmvfc_host *vhost)
1230c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1231c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_event *evt;
1232c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1233c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	BUG_ON(list_empty(&vhost->free));
1234c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	evt = list_entry(vhost->free.next, struct ibmvfc_event, queue);
1235c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	atomic_set(&evt->free, 0);
1236c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	list_del(&evt->queue);
1237c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return evt;
1238c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1239c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1240c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1241c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_init_event - Initialize fields in an event struct that are always
1242c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *				required.
1243c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:	The event
1244c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @done:	Routine to call when the event is responded to
1245c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @format:	SRP or MAD format
1246c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1247c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_init_event(struct ibmvfc_event *evt,
1248c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			      void (*done) (struct ibmvfc_event *), u8 format)
1249c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1250c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	evt->cmnd = NULL;
1251c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	evt->sync_iu = NULL;
1252c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	evt->crq.format = format;
1253c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	evt->done = done;
1254c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	evt->eh_comp = NULL;
1255c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1256c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1257c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1258c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_map_sg_list - Initialize scatterlist
1259c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @scmd:	scsi command struct
1260c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @nseg:	number of scatterlist segments
1261c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @md:	memory descriptor list to initialize
1262c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1263c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_map_sg_list(struct scsi_cmnd *scmd, int nseg,
1264c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			       struct srp_direct_buf *md)
1265c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1266c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int i;
1267c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct scatterlist *sg;
1268c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1269c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	scsi_for_each_sg(scmd, sg, nseg, i) {
1270c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		md[i].va = sg_dma_address(sg);
1271c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		md[i].len = sg_dma_len(sg);
1272c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		md[i].key = 0;
1273c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1274c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1275c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1276c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1277c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_map_sg_data - Maps dma for a scatterlist and initializes decriptor fields
1278c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @scmd:		Scsi_Cmnd with the scatterlist
1279c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:		ibmvfc event struct
1280c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vfc_cmd:	vfc_cmd that contains the memory descriptor
1281c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @dev:		device for which to map dma memory
1282c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1283c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
1284c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 on success / non-zero on failure
1285c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1286c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_map_sg_data(struct scsi_cmnd *scmd,
1287c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			      struct ibmvfc_event *evt,
1288c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			      struct ibmvfc_cmd *vfc_cmd, struct device *dev)
1289c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1290c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1291c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int sg_mapped;
1292c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct srp_direct_buf *data = &vfc_cmd->ioba;
1293c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = dev_get_drvdata(dev);
1294c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1295c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	sg_mapped = scsi_dma_map(scmd);
1296c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!sg_mapped) {
1297c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vfc_cmd->flags |= IBMVFC_NO_MEM_DESC;
1298c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return 0;
1299c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} else if (unlikely(sg_mapped < 0)) {
130064bb336c8f4de8b281d0d44f2ec2c900b9b28466Casey Leedom		if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
1301c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			scmd_printk(KERN_ERR, scmd, "Failed to map DMA buffer for command\n");
1302c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return sg_mapped;
1303c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1304c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1305c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (scmd->sc_data_direction == DMA_TO_DEVICE) {
1306c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vfc_cmd->flags |= IBMVFC_WRITE;
1307c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vfc_cmd->iu.add_cdb_len |= IBMVFC_WRDATA;
1308c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} else {
1309c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vfc_cmd->flags |= IBMVFC_READ;
1310c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vfc_cmd->iu.add_cdb_len |= IBMVFC_RDDATA;
1311c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1312c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1313c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (sg_mapped == 1) {
1314c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_map_sg_list(scmd, sg_mapped, data);
1315c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return 0;
1316c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1317c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1318c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vfc_cmd->flags |= IBMVFC_SCATTERLIST;
1319c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1320c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!evt->ext_list) {
1321c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->ext_list = dma_pool_alloc(vhost->sg_pool, GFP_ATOMIC,
1322c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom					       &evt->ext_list_token);
1323c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1324c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (!evt->ext_list) {
1325c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			scsi_dma_unmap(scmd);
1326c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
1327c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				scmd_printk(KERN_ERR, scmd, "Can't allocate memory for scatterlist\n");
1328c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			return -ENOMEM;
1329c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
1330c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1331c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1332c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_map_sg_list(scmd, sg_mapped, evt->ext_list);
1333c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1334c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	data->va = evt->ext_list_token;
1335c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	data->len = sg_mapped * sizeof(struct srp_direct_buf);
1336c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	data->key = 0;
1337c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return 0;
1338c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1339c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1340c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1341c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_timeout - Internal command timeout handler
1342c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:	struct ibmvfc_event that timed out
1343c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1344c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Called when an internally generated command times out
1345c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1346c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_timeout(struct ibmvfc_event *evt)
1347c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1348c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = evt->vhost;
1349c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	dev_err(vhost->dev, "Command timed out (%p). Resetting connection\n", evt);
1350c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_reset_host(vhost);
1351c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1352c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1353c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1354c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_send_event - Transforms event to u64 array and calls send_crq()
1355c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:		event to be sent
1356c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:		ibmvfc host struct
1357c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @timeout:	timeout in seconds - 0 means do not time command
1358c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1359c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns the value returned from ibmvfc_send_crq(). (Zero for success)
1360c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1361c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_send_event(struct ibmvfc_event *evt,
1362c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			     struct ibmvfc_host *vhost, unsigned long timeout)
1363c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1364c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	u64 *crq_as_u64 = (u64 *) &evt->crq;
1365c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc;
1366c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1367c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	/* Copy the IU into the transfer area */
1368c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	*evt->xfer_iu = evt->iu;
1369c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (evt->crq.format == IBMVFC_CMD_FORMAT)
1370c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->xfer_iu->cmd.tag = (u64)evt;
1371c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	else if (evt->crq.format == IBMVFC_MAD_FORMAT)
1372c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->xfer_iu->mad_common.tag = (u64)evt;
1373c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	else
1374c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		BUG();
1375c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1376c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	list_add_tail(&evt->queue, &vhost->sent);
1377c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	init_timer(&evt->timer);
1378c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1379c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (timeout) {
1380c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->timer.data = (unsigned long) evt;
1381c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->timer.expires = jiffies + (timeout * HZ);
1382c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->timer.function = (void (*)(unsigned long))ibmvfc_timeout;
1383c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		add_timer(&evt->timer);
1384c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1385c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1386c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	mb();
1387c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1388c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if ((rc = ibmvfc_send_crq(vhost, crq_as_u64[0], crq_as_u64[1]))) {
1389c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		list_del(&evt->queue);
1390c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		del_timer(&evt->timer);
1391c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1392c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		/* If send_crq returns H_CLOSED, return SCSI_MLQUEUE_HOST_BUSY.
1393c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 * Firmware will send a CRQ with a transport event (0xFF) to
1394c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 * tell this client what has happened to the transport. This
1395c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 * will be handled in ibmvfc_handle_crq()
1396c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		 */
1397c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (rc == H_CLOSED) {
1398c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if (printk_ratelimit())
1399c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				dev_warn(vhost->dev, "Send warning. Receive queue closed, will retry.\n");
1400c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if (evt->cmnd)
1401c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				scsi_dma_unmap(evt->cmnd);
1402c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			ibmvfc_free_event(evt);
1403c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			return SCSI_MLQUEUE_HOST_BUSY;
1404c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
1405c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1406c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		dev_err(vhost->dev, "Send error (rc=%d)\n", rc);
1407c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (evt->cmnd) {
1408c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			evt->cmnd->result = DID_ERROR << 16;
1409c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			evt->done = ibmvfc_scsi_eh_done;
1410c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		} else
1411c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			evt->xfer_iu->mad_common.status = IBMVFC_MAD_CRQ_ERROR;
1412c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1413c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->done(evt);
1414c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} else
1415c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_trc_start(evt);
1416c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1417c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return 0;
1418c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1419c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1420c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1421c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_log_error - Log an error for the failed command if appropriate
1422c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:	ibmvfc event to log
1423c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1424c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1425c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_log_error(struct ibmvfc_event *evt)
1426c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1427c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
1428c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = evt->vhost;
1429c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
1430c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct scsi_cmnd *cmnd = evt->cmnd;
1431c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	const char *err = unknown_error;
1432c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int index = ibmvfc_get_err_index(vfc_cmd->status, vfc_cmd->error);
1433c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int logerr = 0;
1434c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rsp_code = 0;
1435c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1436c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (index >= 0) {
1437c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		logerr = cmd_status[index].log;
1438c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		err = cmd_status[index].name;
1439c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1440c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1441c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!logerr && (vhost->log_level <= (IBMVFC_DEFAULT_LOG_LEVEL + 1)))
1442c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return;
1443c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1444c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (rsp->flags & FCP_RSP_LEN_VALID)
1445c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rsp_code = rsp->data.info.rsp_code;
1446c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1447c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	scmd_printk(KERN_ERR, cmnd, "Command (%02X) failed: %s (%x:%x) "
1448c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		    "flags: %x fcp_rsp: %x, resid=%d, scsi_status: %x\n",
1449c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		    cmnd->cmnd[0], err, vfc_cmd->status, vfc_cmd->error,
1450c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		    rsp->flags, rsp_code, scsi_get_resid(cmnd), rsp->scsi_status);
1451c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1452c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1453c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1454c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_scsi_done - Handle responses from commands
1455c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:	ibmvfc event to be handled
1456c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1457c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Used as a callback when sending scsi cmds.
1458c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1459c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_scsi_done(struct ibmvfc_event *evt)
1460c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1461c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
1462c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
1463c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct scsi_cmnd *cmnd = evt->cmnd;
1464c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	u32 rsp_len = 0;
1465c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	u32 sense_len = rsp->fcp_sense_len;
1466c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1467c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (cmnd) {
1468c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (vfc_cmd->response_flags & IBMVFC_ADAPTER_RESID_VALID)
1469c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			scsi_set_resid(cmnd, vfc_cmd->adapter_resid);
1470c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		else if (rsp->flags & FCP_RESID_UNDER)
1471c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			scsi_set_resid(cmnd, rsp->fcp_resid);
1472c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		else
1473c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			scsi_set_resid(cmnd, 0);
1474c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1475c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (vfc_cmd->status) {
1476c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			cmnd->result = ibmvfc_get_err_result(vfc_cmd);
1477c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1478c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if (rsp->flags & FCP_RSP_LEN_VALID)
1479c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				rsp_len = rsp->fcp_rsp_len;
1480c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if ((sense_len + rsp_len) > SCSI_SENSE_BUFFERSIZE)
1481c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				sense_len = SCSI_SENSE_BUFFERSIZE - rsp_len;
1482c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len && rsp_len <= 8)
1483c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
1484c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if ((vfc_cmd->status & IBMVFC_VIOS_FAILURE) && (vfc_cmd->error == IBMVFC_PLOGI_REQUIRED))
1485c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				ibmvfc_reinit_host(evt->vhost);
1486c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1487c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
1488c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				cmnd->result = (DID_ERROR << 16);
1489c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1490c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			ibmvfc_log_error(evt);
1491c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
1492c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1493c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (!cmnd->result &&
1494c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		    (scsi_bufflen(cmnd) - scsi_get_resid(cmnd) < cmnd->underflow))
1495c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			cmnd->result = (DID_ERROR << 16);
1496c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1497c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		scsi_dma_unmap(cmnd);
1498c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		cmnd->scsi_done(cmnd);
1499c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1500c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1501c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (evt->eh_comp)
1502c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		complete(evt->eh_comp);
1503c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1504c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_free_event(evt);
1505c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1506c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1507c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1508c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_host_chkready - Check if the host can accept commands
1509c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	 struct ibmvfc host
1510c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1511c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
1512c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	1 if host can accept command / 0 if not
1513c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1514c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic inline int ibmvfc_host_chkready(struct ibmvfc_host *vhost)
1515c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1516c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int result = 0;
1517c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1518c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	switch (vhost->state) {
1519c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_LINK_DEAD:
1520c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HOST_OFFLINE:
1521c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		result = DID_NO_CONNECT << 16;
1522c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
1523c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_NO_CRQ:
1524c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_INITIALIZING:
1525c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_HALTED:
1526c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_LINK_DOWN:
1527c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		result = DID_REQUEUE << 16;
1528c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
1529c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_ACTIVE:
1530c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		result = 0;
1531c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
1532c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	};
1533c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1534c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return result;
1535c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1536c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1537c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1538c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_queuecommand - The queuecommand function of the scsi template
1539c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @cmnd:	struct scsi_cmnd to be executed
1540c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @done:	Callback function to be called when cmnd is completed
1541c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1542c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
1543c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 on success / other on failure
1544c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1545c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_queuecommand(struct scsi_cmnd *cmnd,
1546c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			       void (*done) (struct scsi_cmnd *))
1547c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1548c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(cmnd->device->host);
1549c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
1550c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_cmd *vfc_cmd;
1551c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_event *evt;
1552c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	u8 tag[2];
1553c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc;
1554c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1555c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (unlikely((rc = fc_remote_port_chkready(rport))) ||
1556c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	    unlikely((rc = ibmvfc_host_chkready(vhost)))) {
1557c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		cmnd->result = rc;
1558c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		done(cmnd);
1559c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return 0;
1560c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1561c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1562c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	cmnd->result = (DID_OK << 16);
1563c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	evt = ibmvfc_get_event(vhost);
1564c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_init_event(evt, ibmvfc_scsi_done, IBMVFC_CMD_FORMAT);
1565c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	evt->cmnd = cmnd;
1566c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	cmnd->scsi_done = done;
1567c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vfc_cmd = &evt->iu.cmd;
1568c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	memset(vfc_cmd, 0, sizeof(*vfc_cmd));
1569c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vfc_cmd->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
1570c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vfc_cmd->resp.len = sizeof(vfc_cmd->rsp);
1571c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vfc_cmd->frame_type = IBMVFC_SCSI_FCP_TYPE;
1572c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vfc_cmd->payload_len = sizeof(vfc_cmd->iu);
1573c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vfc_cmd->resp_len = sizeof(vfc_cmd->rsp);
1574c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vfc_cmd->cancel_key = (unsigned long)cmnd->device->hostdata;
1575c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vfc_cmd->tgt_scsi_id = rport->port_id;
1576c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if ((rport->supported_classes & FC_COS_CLASS3) &&
1577c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	    (fc_host_supported_classes(vhost->host) & FC_COS_CLASS3))
1578c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vfc_cmd->flags = IBMVFC_CLASS_3_ERR;
1579c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	vfc_cmd->iu.xfer_len = scsi_bufflen(cmnd);
1580c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int_to_scsilun(cmnd->device->lun, &vfc_cmd->iu.lun);
1581c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	memcpy(vfc_cmd->iu.cdb, cmnd->cmnd, cmnd->cmd_len);
1582c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1583c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (scsi_populate_tag_msg(cmnd, tag)) {
1584c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vfc_cmd->task_tag = tag[1];
1585c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		switch (tag[0]) {
1586c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case MSG_SIMPLE_TAG:
1587c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			vfc_cmd->iu.pri_task_attr = IBMVFC_SIMPLE_TASK;
1588c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
1589c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case MSG_HEAD_TAG:
1590c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			vfc_cmd->iu.pri_task_attr = IBMVFC_HEAD_OF_QUEUE;
1591c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
1592c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case MSG_ORDERED_TAG:
1593c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			vfc_cmd->iu.pri_task_attr = IBMVFC_ORDERED_TASK;
1594c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
1595c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		};
1596c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1597c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1598c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (likely(!(rc = ibmvfc_map_sg_data(cmnd, evt, vfc_cmd, vhost->dev))))
1599c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return ibmvfc_send_event(evt, vhost, 0);
1600c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1601c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_free_event(evt);
1602c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (rc == -ENOMEM)
1603c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return SCSI_MLQUEUE_HOST_BUSY;
1604c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1605c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
1606c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		scmd_printk(KERN_ERR, cmnd,
1607c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			    "Failed to map DMA buffer for command. rc=%d\n", rc);
1608c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1609c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	cmnd->result = DID_ERROR << 16;
1610c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	done(cmnd);
1611c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return 0;
1612c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1613c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1614c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1615c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_sync_completion - Signal that a synchronous command has completed
1616c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:	ibmvfc event struct
1617c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1618c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1619c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_sync_completion(struct ibmvfc_event *evt)
1620c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1621c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	/* copy the response back */
1622c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (evt->sync_iu)
1623c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		*evt->sync_iu = *evt->xfer_iu;
1624c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1625c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	complete(&evt->comp);
1626c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1627c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1628c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1629c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_reset_device - Reset the device with the specified reset type
1630c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @sdev:	scsi device to reset
1631c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @type:	reset type
1632c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @desc:	reset type description for log messages
1633c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1634c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
1635c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 on success / other on failure
1636c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1637c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
1638c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1639c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(sdev->host);
1640c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
1641c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_cmd *tmf;
1642c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_event *evt = NULL;
1643c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	union ibmvfc_iu rsp_iu;
1644c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
1645c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rsp_rc = -EBUSY;
1646c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long flags;
1647c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rsp_code = 0;
1648c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1649c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(vhost->host->host_lock, flags);
1650c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (vhost->state == IBMVFC_ACTIVE) {
1651c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt = ibmvfc_get_event(vhost);
1652c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
1653c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1654c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf = &evt->iu.cmd;
1655c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		memset(tmf, 0, sizeof(*tmf));
1656c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
1657c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->resp.len = sizeof(tmf->rsp);
1658c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
1659c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->payload_len = sizeof(tmf->iu);
1660c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->resp_len = sizeof(tmf->rsp);
1661c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->cancel_key = (unsigned long)sdev->hostdata;
1662c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->tgt_scsi_id = rport->port_id;
1663c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		int_to_scsilun(sdev->lun, &tmf->iu.lun);
1664c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
1665c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->iu.tmf_flags = type;
1666c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->sync_iu = &rsp_iu;
1667c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1668c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		init_completion(&evt->comp);
1669c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
1670c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1671c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(vhost->host->host_lock, flags);
1672c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1673c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (rsp_rc != 0) {
1674c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		sdev_printk(KERN_ERR, sdev, "Failed to send %s reset event. rc=%d\n",
1675c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			    desc, rsp_rc);
1676c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return -EIO;
1677c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1678c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1679c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	sdev_printk(KERN_INFO, sdev, "Resetting %s\n", desc);
1680c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	wait_for_completion(&evt->comp);
1681c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1682c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (rsp_iu.cmd.status) {
1683c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (fc_rsp->flags & FCP_RSP_LEN_VALID)
1684c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			rsp_code = fc_rsp->data.info.rsp_code;
1685c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1686c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		sdev_printk(KERN_ERR, sdev, "%s reset failed: %s (%x:%x) "
1687c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			    "flags: %x fcp_rsp: %x, scsi_status: %x\n",
1688c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			    desc, ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error),
1689c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			    rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
1690c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			    fc_rsp->scsi_status);
1691c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rsp_rc = -EIO;
1692c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} else
1693c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		sdev_printk(KERN_INFO, sdev, "%s reset successful\n", desc);
1694c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1695c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(vhost->host->host_lock, flags);
1696c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_free_event(evt);
1697c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(vhost->host->host_lock, flags);
1698c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return rsp_rc;
1699c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1700c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1701c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1702c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_abort_task_set - Abort outstanding commands to the device
1703c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @sdev:	scsi device to abort commands
1704c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1705c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * This sends an Abort Task Set to the VIOS for the specified device. This does
1706c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * NOT send any cancel to the VIOS. That must be done separately.
1707c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1708c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
1709c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 on success / other on failure
1710c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1711c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_abort_task_set(struct scsi_device *sdev)
1712c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1713c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(sdev->host);
1714c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
1715c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_cmd *tmf;
1716c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_event *evt, *found_evt;
1717c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	union ibmvfc_iu rsp_iu;
1718c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
1719c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rsp_rc = -EBUSY;
1720c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long flags;
1721c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rsp_code = 0;
1722c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1723c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(vhost->host->host_lock, flags);
1724c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	found_evt = NULL;
1725c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	list_for_each_entry(evt, &vhost->sent, queue) {
1726c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (evt->cmnd && evt->cmnd->device == sdev) {
1727c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			found_evt = evt;
1728c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
1729c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
1730c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1731c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1732c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!found_evt) {
1733c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
1734c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			sdev_printk(KERN_INFO, sdev, "No events found to abort\n");
1735c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		spin_unlock_irqrestore(vhost->host->host_lock, flags);
1736c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return 0;
1737c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1738c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1739c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (vhost->state == IBMVFC_ACTIVE) {
1740c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt = ibmvfc_get_event(vhost);
1741c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
1742c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1743c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf = &evt->iu.cmd;
1744c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		memset(tmf, 0, sizeof(*tmf));
1745c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
1746c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->resp.len = sizeof(tmf->rsp);
1747c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
1748c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->payload_len = sizeof(tmf->iu);
1749c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->resp_len = sizeof(tmf->rsp);
1750c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->cancel_key = (unsigned long)sdev->hostdata;
1751c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->tgt_scsi_id = rport->port_id;
1752c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		int_to_scsilun(sdev->lun, &tmf->iu.lun);
1753c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
1754c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET;
1755c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->sync_iu = &rsp_iu;
1756c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1757c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		init_completion(&evt->comp);
1758c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
1759c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1760c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1761c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(vhost->host->host_lock, flags);
1762c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1763c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (rsp_rc != 0) {
1764c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc);
1765c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return -EIO;
1766c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1767c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1768c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n");
1769c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	wait_for_completion(&evt->comp);
1770c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1771c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (rsp_iu.cmd.status) {
1772c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (fc_rsp->flags & FCP_RSP_LEN_VALID)
1773c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			rsp_code = fc_rsp->data.info.rsp_code;
1774c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1775c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) "
1776c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			    "flags: %x fcp_rsp: %x, scsi_status: %x\n",
1777c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			    ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error),
1778c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			    rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
1779c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			    fc_rsp->scsi_status);
1780c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rsp_rc = -EIO;
1781c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} else
1782c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		sdev_printk(KERN_INFO, sdev, "Abort successful\n");
1783c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1784c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(vhost->host->host_lock, flags);
1785c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_free_event(evt);
1786c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(vhost->host->host_lock, flags);
1787c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return rsp_rc;
1788c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1789c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1790c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1791c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_cancel_all - Cancel all outstanding commands to the device
1792c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @sdev:	scsi device to cancel commands
1793c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @type:	type of error recovery being performed
1794c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1795c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * This sends a cancel to the VIOS for the specified device. This does
1796c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * NOT send any abort to the actual device. That must be done separately.
1797c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1798c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
1799c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 on success / other on failure
1800c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1801c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
1802c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1803c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(sdev->host);
1804c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct scsi_target *starget = scsi_target(sdev);
1805c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct fc_rport *rport = starget_to_rport(starget);
1806c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_tmf *tmf;
1807c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_event *evt, *found_evt;
1808c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	union ibmvfc_iu rsp;
1809c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rsp_rc = -EBUSY;
1810c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long flags;
1811c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	u16 status;
1812c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1813c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ENTER;
1814c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(vhost->host->host_lock, flags);
1815c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	found_evt = NULL;
1816c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	list_for_each_entry(evt, &vhost->sent, queue) {
1817c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (evt->cmnd && evt->cmnd->device == sdev) {
1818c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			found_evt = evt;
1819c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
1820c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
1821c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1822c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1823c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!found_evt) {
1824c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
1825c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			sdev_printk(KERN_INFO, sdev, "No events found to cancel\n");
1826c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		spin_unlock_irqrestore(vhost->host->host_lock, flags);
1827c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return 0;
1828c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1829c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1830c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (vhost->state == IBMVFC_ACTIVE) {
1831c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt = ibmvfc_get_event(vhost);
1832c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
1833c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1834c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf = &evt->iu.tmf;
1835c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		memset(tmf, 0, sizeof(*tmf));
1836c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->common.version = 1;
1837c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->common.opcode = IBMVFC_TMF_MAD;
1838c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->common.length = sizeof(*tmf);
1839c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->scsi_id = rport->port_id;
1840c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		int_to_scsilun(sdev->lun, &tmf->lun);
1841c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->flags = (type | IBMVFC_TMF_LUA_VALID);
1842c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->cancel_key = (unsigned long)sdev->hostdata;
1843c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tmf->my_cancel_key = (unsigned long)starget->hostdata;
1844c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1845c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		evt->sync_iu = &rsp;
1846c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		init_completion(&evt->comp);
1847c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
1848c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1849c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1850c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(vhost->host->host_lock, flags);
1851c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1852c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (rsp_rc != 0) {
1853c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		sdev_printk(KERN_ERR, sdev, "Failed to send cancel event. rc=%d\n", rsp_rc);
1854c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return -EIO;
1855c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1856c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1857c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n");
1858c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1859c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	wait_for_completion(&evt->comp);
1860c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	status = rsp.mad_common.status;
1861c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(vhost->host->host_lock, flags);
1862c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_free_event(evt);
1863c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(vhost->host->host_lock, flags);
1864c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1865c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (status != IBMVFC_MAD_SUCCESS) {
1866c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		sdev_printk(KERN_WARNING, sdev, "Cancel failed with rc=%x\n", status);
1867c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return -EIO;
1868c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
1869c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1870c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	sdev_printk(KERN_INFO, sdev, "Successfully cancelled outstanding commands\n");
1871c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return 0;
1872c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1873c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1874c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1875c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_match_target - Match function for specified target
1876c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:	ibmvfc event struct
1877c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @device:	device to match (starget)
1878c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1879c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
1880c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	1 if event matches starget / 0 if event does not match starget
1881c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1882c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_match_target(struct ibmvfc_event *evt, void *device)
1883c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1884c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (evt->cmnd && scsi_target(evt->cmnd->device) == device)
1885c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return 1;
1886c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return 0;
1887c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1888c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1889c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1890c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_match_lun - Match function for specified LUN
1891c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @evt:	ibmvfc event struct
1892c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @device:	device to match (sdev)
1893c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1894c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
1895c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	1 if event matches sdev / 0 if event does not match sdev
1896c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1897c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_match_lun(struct ibmvfc_event *evt, void *device)
1898c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1899c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (evt->cmnd && evt->cmnd->device == device)
1900c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return 1;
1901c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return 0;
1902c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1903c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1904c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1905c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_wait_for_ops - Wait for ops to complete
1906c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
1907c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @device:	device to match (starget or sdev)
1908c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @match:	match function
1909c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1910c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
1911c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	SUCCESS / FAILED
1912c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1913c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device,
1914c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			       int (*match) (struct ibmvfc_event *, void *))
1915c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1916c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_event *evt;
1917c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	DECLARE_COMPLETION_ONSTACK(comp);
1918c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int wait;
1919c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long flags;
1920c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	signed long timeout = init_timeout * HZ;
1921c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1922c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ENTER;
1923c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	do {
1924c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		wait = 0;
1925c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		spin_lock_irqsave(vhost->host->host_lock, flags);
1926c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		list_for_each_entry(evt, &vhost->sent, queue) {
1927c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if (match(evt, device)) {
1928c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				evt->eh_comp = &comp;
1929c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				wait++;
1930c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			}
1931c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
1932c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		spin_unlock_irqrestore(vhost->host->host_lock, flags);
1933c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1934c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (wait) {
1935c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			timeout = wait_for_completion_timeout(&comp, timeout);
1936c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1937c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if (!timeout) {
1938c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				wait = 0;
1939c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				spin_lock_irqsave(vhost->host->host_lock, flags);
1940c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				list_for_each_entry(evt, &vhost->sent, queue) {
1941c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom					if (match(evt, device)) {
1942c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom						evt->eh_comp = NULL;
1943c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom						wait++;
1944c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom					}
1945c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				}
1946c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				spin_unlock_irqrestore(vhost->host->host_lock, flags);
1947c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				if (wait)
1948c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom					dev_err(vhost->dev, "Timed out waiting for aborted commands\n");
1949c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				LEAVE;
1950c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				return wait ? FAILED : SUCCESS;
1951c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			}
1952c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
1953c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} while (wait);
1954c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1955c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	LEAVE;
1956c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return SUCCESS;
1957c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1958c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1959c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1960c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_eh_abort_handler - Abort a command
1961c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @cmd:	scsi command to abort
1962c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1963c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
1964c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	SUCCESS / FAILED
1965c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1966c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
1967c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1968c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct scsi_device *sdev = cmd->device;
1969c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(sdev->host);
1970c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int cancel_rc, abort_rc;
1971c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc = FAILED;
1972c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1973c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ENTER;
1974c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_wait_while_resetting(vhost);
1975c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
1976c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	abort_rc = ibmvfc_abort_task_set(sdev);
1977c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1978c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!cancel_rc && !abort_rc)
1979c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
1980c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1981c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	LEAVE;
1982c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return rc;
1983c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
1984c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1985c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
1986c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_eh_device_reset_handler - Reset a single LUN
1987c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @cmd:	scsi command struct
1988c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
1989c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
1990c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	SUCCESS / FAILED
1991c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
1992c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
1993c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
1994c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct scsi_device *sdev = cmd->device;
1995c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(sdev->host);
1996c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int cancel_rc, reset_rc;
1997c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc = FAILED;
1998c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
1999c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ENTER;
2000c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_wait_while_resetting(vhost);
2001c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	cancel_rc = ibmvfc_cancel_all(sdev, IBMVFC_TMF_LUN_RESET);
2002c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	reset_rc = ibmvfc_reset_device(sdev, IBMVFC_LUN_RESET, "LUN");
2003c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2004c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!cancel_rc && !reset_rc)
2005c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rc = ibmvfc_wait_for_ops(vhost, sdev, ibmvfc_match_lun);
2006c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2007c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	LEAVE;
2008c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return rc;
2009c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2010c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2011c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2012c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_dev_cancel_all - Device iterated cancel all function
2013c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @sdev:	scsi device struct
2014c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @data:	return code
2015c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2016c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2017c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_dev_cancel_all(struct scsi_device *sdev, void *data)
2018c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2019c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long *rc = data;
2020c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	*rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET);
2021c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2022c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2023c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2024c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_dev_abort_all - Device iterated abort task set function
2025c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @sdev:	scsi device struct
2026c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @data:	return code
2027c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2028c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2029c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data)
2030c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2031c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long *rc = data;
2032c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	*rc |= ibmvfc_abort_task_set(sdev);
2033c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2034c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2035c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2036c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_eh_target_reset_handler - Reset the target
2037c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @cmd:	scsi command struct
2038c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2039c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
2040c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	SUCCESS / FAILED
2041c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2042c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
2043c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2044c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct scsi_device *sdev = cmd->device;
2045c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(sdev->host);
2046c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct scsi_target *starget = scsi_target(sdev);
2047c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int reset_rc;
2048c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc = FAILED;
2049c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long cancel_rc = 0;
2050c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2051c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ENTER;
2052c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_wait_while_resetting(vhost);
2053c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
2054c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
2055c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2056c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!cancel_rc && !reset_rc)
2057c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
2058c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2059c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	LEAVE;
2060c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return rc;
2061c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2062c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2063c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2064c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_eh_host_reset_handler - Reset the connection to the server
2065c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @cmd:	struct scsi_cmnd having problems
2066c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2067c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2068c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd)
2069c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2070c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc;
2071c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
2072c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2073c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	dev_err(vhost->dev, "Resetting connection due to error recovery\n");
2074c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	rc = ibmvfc_issue_fc_host_lip(vhost->host);
2075c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return rc ? FAILED : SUCCESS;
2076c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2077c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2078c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2079c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_terminate_rport_io - Terminate all pending I/O to the rport.
2080c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @rport:		rport struct
2081c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2082c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
2083c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * 	none
2084c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2085c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_terminate_rport_io(struct fc_rport *rport)
2086c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2087c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct scsi_target *starget = to_scsi_target(&rport->dev);
2088c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
2089c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(shost);
2090c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long cancel_rc = 0;
2091c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long abort_rc = 0;
2092c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int rc = FAILED;
2093c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2094c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ENTER;
2095c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
2096c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all);
2097c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2098c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!cancel_rc && !abort_rc)
2099c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		rc = ibmvfc_wait_for_ops(vhost, starget, ibmvfc_match_target);
2100c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2101c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (rc == FAILED)
2102c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_issue_fc_host_lip(shost);
2103c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	LEAVE;
2104c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2105c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2106c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const struct {
2107c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	enum ibmvfc_async_event ae;
2108c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	const char *desc;
2109c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom} ae_desc [] = {
2110c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_ELS_PLOGI,		"PLOGI" },
2111c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_ELS_LOGO,		"LOGO" },
2112c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_ELS_PRLO,		"PRLO" },
2113c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_SCN_NPORT,		"N-Port SCN" },
2114c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_SCN_GROUP,		"Group SCN" },
2115c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_SCN_DOMAIN,		"Domain SCN" },
2116c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_SCN_FABRIC,		"Fabric SCN" },
2117c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_LINK_UP,		"Link Up" },
2118c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_LINK_DOWN,		"Link Down" },
2119c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_LINK_DEAD,		"Link Dead" },
2120c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_HALT,			"Halt" },
2121c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_RESUME,		"Resume" },
2122c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	{ IBMVFC_AE_ADAPTER_FAILED,	"Adapter Failed" },
2123c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom};
2124c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2125c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const char *unknown_ae = "Unknown async";
2126c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2127c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2128c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_get_ae_desc - Get text description for async event
2129c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @ae:	async event
2130c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2131c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2132c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic const char *ibmvfc_get_ae_desc(u64 ae)
2133c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2134c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int i;
2135c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2136c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	for (i = 0; i < ARRAY_SIZE(ae_desc); i++)
2137c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (ae_desc[i].ae == ae)
2138c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			return ae_desc[i].desc;
2139c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2140c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return unknown_ae;
2141c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2142c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2143c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2144c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_handle_async - Handle an async event from the adapter
2145c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @crq:	crq to process
2146c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
2147c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2148c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2149c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
2150c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				struct ibmvfc_host *vhost)
2151c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2152c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	const char *desc = ibmvfc_get_ae_desc(crq->event);
2153c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2154c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_log(vhost, 3, "%s event received. scsi_id: %llx, wwpn: %llx,"
2155c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		   " node_name: %llx\n", desc, crq->scsi_id, crq->wwpn, crq->node_name);
2156c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2157c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	switch (crq->event) {
2158c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_LINK_UP:
2159c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_RESUME:
2160c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vhost->events_to_log |= IBMVFC_AE_LINKUP;
2161c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vhost->delay_init = 1;
2162c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		__ibmvfc_reset_host(vhost);
2163c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
2164c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_SCN_FABRIC:
2165c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_SCN_DOMAIN:
2166c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vhost->events_to_log |= IBMVFC_AE_RSCN;
2167c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vhost->delay_init = 1;
2168c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		__ibmvfc_reset_host(vhost);
2169c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
2170c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_SCN_NPORT:
2171c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_SCN_GROUP:
2172c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vhost->events_to_log |= IBMVFC_AE_RSCN;
2173c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_ELS_LOGO:
2174c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_ELS_PRLO:
2175c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_ELS_PLOGI:
2176c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_reinit_host(vhost);
2177c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
2178c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_LINK_DOWN:
2179c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_ADAPTER_FAILED:
2180c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
2181c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
2182c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_LINK_DEAD:
2183c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
2184c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
2185c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_AE_HALT:
2186c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_link_down(vhost, IBMVFC_HALTED);
2187c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
2188c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	default:
2189c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		dev_err(vhost->dev, "Unknown async event received: %lld\n", crq->event);
2190c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
2191c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	};
2192c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2193c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2194c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2195c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_handle_crq - Handles and frees received events in the CRQ
2196c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @crq:	Command/Response queue
2197c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @vhost:	ibmvfc host struct
2198c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2199c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2200c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
2201c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2202c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	long rc;
2203c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_event *evt = (struct ibmvfc_event *)crq->ioba;
2204c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2205c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	switch (crq->valid) {
2206c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_CRQ_INIT_RSP:
2207c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		switch (crq->format) {
2208c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case IBMVFC_CRQ_INIT:
2209c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			dev_info(vhost->dev, "Partner initialized\n");
2210c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			/* Send back a response */
2211c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			rc = ibmvfc_send_crq_init_complete(vhost);
2212c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if (rc == 0)
2213c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				ibmvfc_init_host(vhost, 0);
2214c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			else
2215c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				dev_err(vhost->dev, "Unable to send init rsp. rc=%ld\n", rc);
2216c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
2217c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		case IBMVFC_CRQ_INIT_COMPLETE:
2218c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			dev_info(vhost->dev, "Partner initialization complete\n");
2219c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			ibmvfc_init_host(vhost, 0);
2220c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			break;
2221c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		default:
2222c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			dev_err(vhost->dev, "Unknown crq message type: %d\n", crq->format);
2223c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
2224c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return;
2225c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_CRQ_XPORT_EVENT:
2226c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		vhost->state = IBMVFC_NO_CRQ;
2227c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
2228c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (crq->format == IBMVFC_PARTITION_MIGRATED) {
2229c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			/* We need to re-setup the interpartition connection */
2230c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			dev_info(vhost->dev, "Re-enabling adapter\n");
2231c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			vhost->client_migrated = 1;
2232c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			ibmvfc_purge_requests(vhost, DID_REQUEUE);
2233c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if ((rc = ibmvfc_reenable_crq_queue(vhost)) ||
2234c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			    (rc = ibmvfc_send_crq_init(vhost))) {
2235c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
2236c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				dev_err(vhost->dev, "Error after enable (rc=%ld)\n", rc);
2237c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			} else
2238c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
2239c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		} else {
2240c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			dev_err(vhost->dev, "Virtual adapter failed (rc=%d)\n", crq->format);
2241c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2242c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			ibmvfc_purge_requests(vhost, DID_ERROR);
2243c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			if ((rc = ibmvfc_reset_crq(vhost)) ||
2244c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			    (rc = ibmvfc_send_crq_init(vhost))) {
2245c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
2246c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				dev_err(vhost->dev, "Error after reset (rc=%ld)\n", rc);
2247c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			} else
2248c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom				ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
2249c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		}
2250c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return;
2251c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	case IBMVFC_CRQ_CMD_RSP:
2252c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		break;
2253c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	default:
2254c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		dev_err(vhost->dev, "Got an invalid message type 0x%02x\n", crq->valid);
2255c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return;
2256c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
2257c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2258c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (crq->format == IBMVFC_ASYNC_EVENT)
2259c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return;
2260c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2261c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	/* The only kind of payload CRQs we should get are responses to
2262c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	 * things we send. Make sure this response is to something we
2263c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	 * actually sent
2264c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	 */
2265c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (unlikely(!ibmvfc_valid_event(&vhost->pool, evt))) {
2266c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		dev_err(vhost->dev, "Returned correlation_token 0x%08llx is invalid!\n",
2267c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			crq->ioba);
2268c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return;
2269c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
2270c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2271c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (unlikely(atomic_read(&evt->free))) {
2272c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		dev_err(vhost->dev, "Received duplicate correlation_token 0x%08llx!\n",
2273c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			crq->ioba);
2274c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return;
2275c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
2276c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2277c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	del_timer(&evt->timer);
2278c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	list_del(&evt->queue);
2279c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	ibmvfc_trc_end(evt);
2280c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	evt->done(evt);
2281c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2282c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2283c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2284c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_scan_finished - Check if the device scan is done.
2285c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @shost:	scsi host struct
2286c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @time:	current elapsed time
2287c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2288c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
2289c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 if scan is not done / 1 if scan is done
2290c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2291c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
2292c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2293c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long flags;
2294c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(shost);
2295c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	int done = 0;
2296c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2297c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(shost->host_lock, flags);
2298c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (time >= (init_timeout * HZ)) {
2299c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		dev_info(vhost->dev, "Scan taking longer than %d seconds, "
2300c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			 "continuing initialization\n", init_timeout);
2301c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		done = 1;
2302c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	}
2303c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2304c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (vhost->state != IBMVFC_NO_CRQ && vhost->action == IBMVFC_HOST_ACTION_NONE)
2305c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		done = 1;
2306c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(shost->host_lock, flags);
2307c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return done;
2308c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2309c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2310c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2311c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_slave_alloc - Setup the device's task set value
2312c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @sdev:	struct scsi_device device to configure
2313c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2314c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Set the device's task set value so that error handling works as
2315c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * expected.
2316c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2317c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
2318c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 on success / -ENXIO if device does not exist
2319c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2320c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_slave_alloc(struct scsi_device *sdev)
2321c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2322c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct Scsi_Host *shost = sdev->host;
2323c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
2324c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(shost);
2325c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long flags = 0;
2326c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2327c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (!rport || fc_remote_port_chkready(rport))
2328c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		return -ENXIO;
2329c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2330c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(shost->host_lock, flags);
2331c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	sdev->hostdata = (void *)(unsigned long)vhost->task_set++;
2332c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(shost->host_lock, flags);
2333c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return 0;
2334c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2335c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2336c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2337c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_target_alloc - Setup the target's task set value
2338c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @starget:	struct scsi_target
2339c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2340c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Set the target's task set value so that error handling works as
2341c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * expected.
2342c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2343c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
2344c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0 on success / -ENXIO if device does not exist
2345c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2346c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_target_alloc(struct scsi_target *starget)
2347c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2348c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
2349c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(shost);
2350c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long flags = 0;
2351c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2352c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(shost->host_lock, flags);
2353c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	starget->hostdata = (void *)(unsigned long)vhost->task_set++;
2354c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(shost->host_lock, flags);
2355c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return 0;
2356c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2357c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2358c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2359c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_slave_configure - Configure the device
2360c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @sdev:	struct scsi_device device to configure
2361c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2362c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Enable allow_restart for a device if it is a disk. Adjust the
2363c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * queue_depth here also.
2364c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2365c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Returns:
2366c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *	0
2367c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2368c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_slave_configure(struct scsi_device *sdev)
2369c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2370c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct Scsi_Host *shost = sdev->host;
2371c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
2372c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	unsigned long flags = 0;
2373c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2374c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_lock_irqsave(shost->host_lock, flags);
2375c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (sdev->type == TYPE_DISK)
2376c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		sdev->allow_restart = 1;
2377c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2378c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (sdev->tagged_supported) {
2379c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
2380c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		scsi_activate_tcq(sdev, sdev->queue_depth);
2381c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} else
2382c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		scsi_deactivate_tcq(sdev, sdev->queue_depth);
2383c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2384c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	rport->dev_loss_tmo = dev_loss_tmo;
2385c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	spin_unlock_irqrestore(shost->host_lock, flags);
2386c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return 0;
2387c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2388c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2389c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2390c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_change_queue_depth - Change the device's queue depth
2391c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @sdev:	scsi device struct
2392c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @qdepth:	depth to set
2393c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2394c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
2395c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * 	actual depth set
2396c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2397c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth)
2398c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2399c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (qdepth > IBMVFC_MAX_CMDS_PER_LUN)
2400c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		qdepth = IBMVFC_MAX_CMDS_PER_LUN;
2401c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2402c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	scsi_adjust_queue_depth(sdev, 0, qdepth);
2403c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return sdev->queue_depth;
2404c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2405c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2406c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom/**
2407c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * ibmvfc_change_queue_type - Change the device's queue type
2408c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @sdev:		scsi device struct
2409c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * @tag_type:	type of tags to use
2410c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom *
2411c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * Return value:
2412c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom * 	actual queue type set
2413c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom **/
2414c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic int ibmvfc_change_queue_type(struct scsi_device *sdev, int tag_type)
2415c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom{
2416c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	if (sdev->tagged_supported) {
2417c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		scsi_set_tag_type(sdev, tag_type);
2418c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2419c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		if (tag_type)
2420c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			scsi_activate_tcq(sdev, sdev->queue_depth);
2421c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		else
2422c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			scsi_deactivate_tcq(sdev, sdev->queue_depth);
2423c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	} else
2424c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		tag_type = 0;
2425c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2426c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return tag_type;
2427c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2428c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2429c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic ssize_t ibmvfc_show_host_partition_name(struct device *dev,
2430c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom						 struct device_attribute *attr, char *buf)
2431b3003be36a3c9215cd17182349981581de269048Casey Leedom{
2432c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct Scsi_Host *shost = class_to_shost(dev);
2433c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	struct ibmvfc_host *vhost = shost_priv(shost);
2434c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2435c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	return snprintf(buf, PAGE_SIZE, "%s\n",
2436c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom			vhost->login_buf->resp.partition_name);
2437c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom}
2438c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom
2439c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedomstatic struct device_attribute ibmvfc_host_partition_name = {
2440c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	.attr = {
2441c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		.name = "partition_name",
2442c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom		.mode = S_IRUGO,
2443c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	},
2444c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom	.show = ibmvfc_show_host_partition_name,
2445c6e0d91464da214081af546496dd3a4b6d19db70Casey Leedom};
2446
2447static ssize_t ibmvfc_show_host_device_name(struct device *dev,
2448					    struct device_attribute *attr, char *buf)
2449{
2450	struct Scsi_Host *shost = class_to_shost(dev);
2451	struct ibmvfc_host *vhost = shost_priv(shost);
2452
2453	return snprintf(buf, PAGE_SIZE, "%s\n",
2454			vhost->login_buf->resp.device_name);
2455}
2456
2457static struct device_attribute ibmvfc_host_device_name = {
2458	.attr = {
2459		.name = "device_name",
2460		.mode = S_IRUGO,
2461	},
2462	.show = ibmvfc_show_host_device_name,
2463};
2464
2465static ssize_t ibmvfc_show_host_loc_code(struct device *dev,
2466					 struct device_attribute *attr, char *buf)
2467{
2468	struct Scsi_Host *shost = class_to_shost(dev);
2469	struct ibmvfc_host *vhost = shost_priv(shost);
2470
2471	return snprintf(buf, PAGE_SIZE, "%s\n",
2472			vhost->login_buf->resp.port_loc_code);
2473}
2474
2475static struct device_attribute ibmvfc_host_loc_code = {
2476	.attr = {
2477		.name = "port_loc_code",
2478		.mode = S_IRUGO,
2479	},
2480	.show = ibmvfc_show_host_loc_code,
2481};
2482
2483static ssize_t ibmvfc_show_host_drc_name(struct device *dev,
2484					 struct device_attribute *attr, char *buf)
2485{
2486	struct Scsi_Host *shost = class_to_shost(dev);
2487	struct ibmvfc_host *vhost = shost_priv(shost);
2488
2489	return snprintf(buf, PAGE_SIZE, "%s\n",
2490			vhost->login_buf->resp.drc_name);
2491}
2492
2493static struct device_attribute ibmvfc_host_drc_name = {
2494	.attr = {
2495		.name = "drc_name",
2496		.mode = S_IRUGO,
2497	},
2498	.show = ibmvfc_show_host_drc_name,
2499};
2500
2501static ssize_t ibmvfc_show_host_npiv_version(struct device *dev,
2502					     struct device_attribute *attr, char *buf)
2503{
2504	struct Scsi_Host *shost = class_to_shost(dev);
2505	struct ibmvfc_host *vhost = shost_priv(shost);
2506	return snprintf(buf, PAGE_SIZE, "%d\n", vhost->login_buf->resp.version);
2507}
2508
2509static struct device_attribute ibmvfc_host_npiv_version = {
2510	.attr = {
2511		.name = "npiv_version",
2512		.mode = S_IRUGO,
2513	},
2514	.show = ibmvfc_show_host_npiv_version,
2515};
2516
2517/**
2518 * ibmvfc_show_log_level - Show the adapter's error logging level
2519 * @dev:	class device struct
2520 * @buf:	buffer
2521 *
2522 * Return value:
2523 * 	number of bytes printed to buffer
2524 **/
2525static ssize_t ibmvfc_show_log_level(struct device *dev,
2526				     struct device_attribute *attr, char *buf)
2527{
2528	struct Scsi_Host *shost = class_to_shost(dev);
2529	struct ibmvfc_host *vhost = shost_priv(shost);
2530	unsigned long flags = 0;
2531	int len;
2532
2533	spin_lock_irqsave(shost->host_lock, flags);
2534	len = snprintf(buf, PAGE_SIZE, "%d\n", vhost->log_level);
2535	spin_unlock_irqrestore(shost->host_lock, flags);
2536	return len;
2537}
2538
2539/**
2540 * ibmvfc_store_log_level - Change the adapter's error logging level
2541 * @dev:	class device struct
2542 * @buf:	buffer
2543 *
2544 * Return value:
2545 * 	number of bytes printed to buffer
2546 **/
2547static ssize_t ibmvfc_store_log_level(struct device *dev,
2548				      struct device_attribute *attr,
2549				      const char *buf, size_t count)
2550{
2551	struct Scsi_Host *shost = class_to_shost(dev);
2552	struct ibmvfc_host *vhost = shost_priv(shost);
2553	unsigned long flags = 0;
2554
2555	spin_lock_irqsave(shost->host_lock, flags);
2556	vhost->log_level = simple_strtoul(buf, NULL, 10);
2557	spin_unlock_irqrestore(shost->host_lock, flags);
2558	return strlen(buf);
2559}
2560
2561static struct device_attribute ibmvfc_log_level_attr = {
2562	.attr = {
2563		.name =		"log_level",
2564		.mode =		S_IRUGO | S_IWUSR,
2565	},
2566	.show = ibmvfc_show_log_level,
2567	.store = ibmvfc_store_log_level
2568};
2569
2570#ifdef CONFIG_SCSI_IBMVFC_TRACE
2571/**
2572 * ibmvfc_read_trace - Dump the adapter trace
2573 * @kobj:		kobject struct
2574 * @bin_attr:	bin_attribute struct
2575 * @buf:		buffer
2576 * @off:		offset
2577 * @count:		buffer size
2578 *
2579 * Return value:
2580 *	number of bytes printed to buffer
2581 **/
2582static ssize_t ibmvfc_read_trace(struct kobject *kobj,
2583				 struct bin_attribute *bin_attr,
2584				 char *buf, loff_t off, size_t count)
2585{
2586	struct device *dev = container_of(kobj, struct device, kobj);
2587	struct Scsi_Host *shost = class_to_shost(dev);
2588	struct ibmvfc_host *vhost = shost_priv(shost);
2589	unsigned long flags = 0;
2590	int size = IBMVFC_TRACE_SIZE;
2591	char *src = (char *)vhost->trace;
2592
2593	if (off > size)
2594		return 0;
2595	if (off + count > size) {
2596		size -= off;
2597		count = size;
2598	}
2599
2600	spin_lock_irqsave(shost->host_lock, flags);
2601	memcpy(buf, &src[off], count);
2602	spin_unlock_irqrestore(shost->host_lock, flags);
2603	return count;
2604}
2605
2606static struct bin_attribute ibmvfc_trace_attr = {
2607	.attr =	{
2608		.name = "trace",
2609		.mode = S_IRUGO,
2610	},
2611	.size = 0,
2612	.read = ibmvfc_read_trace,
2613};
2614#endif
2615
2616static struct device_attribute *ibmvfc_attrs[] = {
2617	&ibmvfc_host_partition_name,
2618	&ibmvfc_host_device_name,
2619	&ibmvfc_host_loc_code,
2620	&ibmvfc_host_drc_name,
2621	&ibmvfc_host_npiv_version,
2622	&ibmvfc_log_level_attr,
2623	NULL
2624};
2625
2626static struct scsi_host_template driver_template = {
2627	.module = THIS_MODULE,
2628	.name = "IBM POWER Virtual FC Adapter",
2629	.proc_name = IBMVFC_NAME,
2630	.queuecommand = ibmvfc_queuecommand,
2631	.eh_abort_handler = ibmvfc_eh_abort_handler,
2632	.eh_device_reset_handler = ibmvfc_eh_device_reset_handler,
2633	.eh_target_reset_handler = ibmvfc_eh_target_reset_handler,
2634	.eh_host_reset_handler = ibmvfc_eh_host_reset_handler,
2635	.slave_alloc = ibmvfc_slave_alloc,
2636	.slave_configure = ibmvfc_slave_configure,
2637	.target_alloc = ibmvfc_target_alloc,
2638	.scan_finished = ibmvfc_scan_finished,
2639	.change_queue_depth = ibmvfc_change_queue_depth,
2640	.change_queue_type = ibmvfc_change_queue_type,
2641	.cmd_per_lun = 16,
2642	.can_queue = IBMVFC_MAX_REQUESTS_DEFAULT,
2643	.this_id = -1,
2644	.sg_tablesize = SG_ALL,
2645	.max_sectors = IBMVFC_MAX_SECTORS,
2646	.use_clustering = ENABLE_CLUSTERING,
2647	.shost_attrs = ibmvfc_attrs,
2648};
2649
2650/**
2651 * ibmvfc_next_async_crq - Returns the next entry in async queue
2652 * @vhost:	ibmvfc host struct
2653 *
2654 * Returns:
2655 *	Pointer to next entry in queue / NULL if empty
2656 **/
2657static struct ibmvfc_async_crq *ibmvfc_next_async_crq(struct ibmvfc_host *vhost)
2658{
2659	struct ibmvfc_async_crq_queue *async_crq = &vhost->async_crq;
2660	struct ibmvfc_async_crq *crq;
2661
2662	crq = &async_crq->msgs[async_crq->cur];
2663	if (crq->valid & 0x80) {
2664		if (++async_crq->cur == async_crq->size)
2665			async_crq->cur = 0;
2666	} else
2667		crq = NULL;
2668
2669	return crq;
2670}
2671
2672/**
2673 * ibmvfc_next_crq - Returns the next entry in message queue
2674 * @vhost:	ibmvfc host struct
2675 *
2676 * Returns:
2677 *	Pointer to next entry in queue / NULL if empty
2678 **/
2679static struct ibmvfc_crq *ibmvfc_next_crq(struct ibmvfc_host *vhost)
2680{
2681	struct ibmvfc_crq_queue *queue = &vhost->crq;
2682	struct ibmvfc_crq *crq;
2683
2684	crq = &queue->msgs[queue->cur];
2685	if (crq->valid & 0x80) {
2686		if (++queue->cur == queue->size)
2687			queue->cur = 0;
2688	} else
2689		crq = NULL;
2690
2691	return crq;
2692}
2693
2694/**
2695 * ibmvfc_interrupt - Interrupt handler
2696 * @irq:		number of irq to handle, not used
2697 * @dev_instance: ibmvfc_host that received interrupt
2698 *
2699 * Returns:
2700 *	IRQ_HANDLED
2701 **/
2702static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance)
2703{
2704	struct ibmvfc_host *vhost = (struct ibmvfc_host *)dev_instance;
2705	struct vio_dev *vdev = to_vio_dev(vhost->dev);
2706	struct ibmvfc_crq *crq;
2707	struct ibmvfc_async_crq *async;
2708	unsigned long flags;
2709	int done = 0;
2710
2711	spin_lock_irqsave(vhost->host->host_lock, flags);
2712	vio_disable_interrupts(to_vio_dev(vhost->dev));
2713	while (!done) {
2714		/* Pull all the valid messages off the CRQ */
2715		while ((crq = ibmvfc_next_crq(vhost)) != NULL) {
2716			ibmvfc_handle_crq(crq, vhost);
2717			crq->valid = 0;
2718		}
2719
2720		/* Pull all the valid messages off the async CRQ */
2721		while ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
2722			ibmvfc_handle_async(async, vhost);
2723			async->valid = 0;
2724		}
2725
2726		vio_enable_interrupts(vdev);
2727		if ((crq = ibmvfc_next_crq(vhost)) != NULL) {
2728			vio_disable_interrupts(vdev);
2729			ibmvfc_handle_crq(crq, vhost);
2730			crq->valid = 0;
2731		} else if ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
2732			vio_disable_interrupts(vdev);
2733			ibmvfc_handle_async(async, vhost);
2734			async->valid = 0;
2735		} else
2736			done = 1;
2737	}
2738
2739	spin_unlock_irqrestore(vhost->host->host_lock, flags);
2740	return IRQ_HANDLED;
2741}
2742
2743/**
2744 * ibmvfc_init_tgt - Set the next init job step for the target
2745 * @tgt:		ibmvfc target struct
2746 * @job_step:	job step to perform
2747 *
2748 **/
2749static void ibmvfc_init_tgt(struct ibmvfc_target *tgt,
2750			    void (*job_step) (struct ibmvfc_target *))
2751{
2752	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT);
2753	tgt->job_step = job_step;
2754	wake_up(&tgt->vhost->work_wait_q);
2755}
2756
2757/**
2758 * ibmvfc_retry_tgt_init - Attempt to retry a step in target initialization
2759 * @tgt:		ibmvfc target struct
2760 * @job_step:	initialization job step
2761 *
2762 **/
2763static void ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
2764				  void (*job_step) (struct ibmvfc_target *))
2765{
2766	if (++tgt->init_retries > IBMVFC_MAX_TGT_INIT_RETRIES) {
2767		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
2768		wake_up(&tgt->vhost->work_wait_q);
2769	} else
2770		ibmvfc_init_tgt(tgt, job_step);
2771}
2772
2773/**
2774 * ibmvfc_tgt_prli_done - Completion handler for Process Login
2775 * @evt:	ibmvfc event struct
2776 *
2777 **/
2778static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
2779{
2780	struct ibmvfc_target *tgt = evt->tgt;
2781	struct ibmvfc_host *vhost = evt->vhost;
2782	struct ibmvfc_process_login *rsp = &evt->xfer_iu->prli;
2783	u32 status = rsp->common.status;
2784
2785	vhost->discovery_threads--;
2786	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
2787	switch (status) {
2788	case IBMVFC_MAD_SUCCESS:
2789		tgt_dbg(tgt, "Process Login succeeded\n");
2790		tgt->need_login = 0;
2791		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_ADD_RPORT);
2792		break;
2793	case IBMVFC_MAD_DRIVER_FAILED:
2794		break;
2795	case IBMVFC_MAD_CRQ_ERROR:
2796		ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
2797		break;
2798	case IBMVFC_MAD_FAILED:
2799	default:
2800		tgt_err(tgt, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
2801			ibmvfc_get_cmd_error(rsp->status, rsp->error),
2802			rsp->status, rsp->error, status);
2803		if (ibmvfc_retry_cmd(rsp->status, rsp->error))
2804			ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
2805		else
2806			ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
2807		break;
2808	};
2809
2810	kref_put(&tgt->kref, ibmvfc_release_tgt);
2811	ibmvfc_free_event(evt);
2812	wake_up(&vhost->work_wait_q);
2813}
2814
2815/**
2816 * ibmvfc_tgt_send_prli - Send a process login
2817 * @tgt:	ibmvfc target struct
2818 *
2819 **/
2820static void ibmvfc_tgt_send_prli(struct ibmvfc_target *tgt)
2821{
2822	struct ibmvfc_process_login *prli;
2823	struct ibmvfc_host *vhost = tgt->vhost;
2824	struct ibmvfc_event *evt;
2825
2826	if (vhost->discovery_threads >= disc_threads)
2827		return;
2828
2829	kref_get(&tgt->kref);
2830	evt = ibmvfc_get_event(vhost);
2831	vhost->discovery_threads++;
2832	ibmvfc_init_event(evt, ibmvfc_tgt_prli_done, IBMVFC_MAD_FORMAT);
2833	evt->tgt = tgt;
2834	prli = &evt->iu.prli;
2835	memset(prli, 0, sizeof(*prli));
2836	prli->common.version = 1;
2837	prli->common.opcode = IBMVFC_PROCESS_LOGIN;
2838	prli->common.length = sizeof(*prli);
2839	prli->scsi_id = tgt->scsi_id;
2840
2841	prli->parms.type = IBMVFC_SCSI_FCP_TYPE;
2842	prli->parms.flags = IBMVFC_PRLI_EST_IMG_PAIR;
2843	prli->parms.service_parms = IBMVFC_PRLI_INITIATOR_FUNC;
2844
2845	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
2846	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
2847		vhost->discovery_threads--;
2848		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
2849		kref_put(&tgt->kref, ibmvfc_release_tgt);
2850	} else
2851		tgt_dbg(tgt, "Sent process login\n");
2852}
2853
2854/**
2855 * ibmvfc_tgt_plogi_done - Completion handler for Port Login
2856 * @evt:	ibmvfc event struct
2857 *
2858 **/
2859static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
2860{
2861	struct ibmvfc_target *tgt = evt->tgt;
2862	struct ibmvfc_host *vhost = evt->vhost;
2863	struct ibmvfc_port_login *rsp = &evt->xfer_iu->plogi;
2864	u32 status = rsp->common.status;
2865
2866	vhost->discovery_threads--;
2867	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
2868	switch (status) {
2869	case IBMVFC_MAD_SUCCESS:
2870		tgt_dbg(tgt, "Port Login succeeded\n");
2871		if (tgt->ids.port_name &&
2872		    tgt->ids.port_name != wwn_to_u64(rsp->service_parms.port_name)) {
2873			vhost->reinit = 1;
2874			tgt_dbg(tgt, "Port re-init required\n");
2875			break;
2876		}
2877		tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name);
2878		tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name);
2879		tgt->ids.port_id = tgt->scsi_id;
2880		tgt->ids.roles = FC_PORT_ROLE_FCP_TARGET;
2881		memcpy(&tgt->service_parms, &rsp->service_parms,
2882		       sizeof(tgt->service_parms));
2883		memcpy(&tgt->service_parms_change, &rsp->service_parms_change,
2884		       sizeof(tgt->service_parms_change));
2885		ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_prli);
2886		break;
2887	case IBMVFC_MAD_DRIVER_FAILED:
2888		break;
2889	case IBMVFC_MAD_CRQ_ERROR:
2890		ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
2891		break;
2892	case IBMVFC_MAD_FAILED:
2893	default:
2894		tgt_err(tgt, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
2895			ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
2896			ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
2897			ibmvfc_get_ls_explain(rsp->fc_explain), rsp->fc_explain, status);
2898
2899		if (ibmvfc_retry_cmd(rsp->status, rsp->error))
2900			ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
2901		else
2902			ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
2903		break;
2904	};
2905
2906	kref_put(&tgt->kref, ibmvfc_release_tgt);
2907	ibmvfc_free_event(evt);
2908	wake_up(&vhost->work_wait_q);
2909}
2910
2911/**
2912 * ibmvfc_tgt_send_plogi - Send PLOGI to the specified target
2913 * @tgt:	ibmvfc target struct
2914 *
2915 **/
2916static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt)
2917{
2918	struct ibmvfc_port_login *plogi;
2919	struct ibmvfc_host *vhost = tgt->vhost;
2920	struct ibmvfc_event *evt;
2921
2922	if (vhost->discovery_threads >= disc_threads)
2923		return;
2924
2925	kref_get(&tgt->kref);
2926	evt = ibmvfc_get_event(vhost);
2927	vhost->discovery_threads++;
2928	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
2929	ibmvfc_init_event(evt, ibmvfc_tgt_plogi_done, IBMVFC_MAD_FORMAT);
2930	evt->tgt = tgt;
2931	plogi = &evt->iu.plogi;
2932	memset(plogi, 0, sizeof(*plogi));
2933	plogi->common.version = 1;
2934	plogi->common.opcode = IBMVFC_PORT_LOGIN;
2935	plogi->common.length = sizeof(*plogi);
2936	plogi->scsi_id = tgt->scsi_id;
2937
2938	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
2939		vhost->discovery_threads--;
2940		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
2941		kref_put(&tgt->kref, ibmvfc_release_tgt);
2942	} else
2943		tgt_dbg(tgt, "Sent port login\n");
2944}
2945
2946/**
2947 * ibmvfc_tgt_implicit_logout_done - Completion handler for Implicit Logout MAD
2948 * @evt:	ibmvfc event struct
2949 *
2950 **/
2951static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt)
2952{
2953	struct ibmvfc_target *tgt = evt->tgt;
2954	struct ibmvfc_host *vhost = evt->vhost;
2955	struct ibmvfc_implicit_logout *rsp = &evt->xfer_iu->implicit_logout;
2956	u32 status = rsp->common.status;
2957
2958	vhost->discovery_threads--;
2959	ibmvfc_free_event(evt);
2960	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
2961
2962	switch (status) {
2963	case IBMVFC_MAD_SUCCESS:
2964		tgt_dbg(tgt, "Implicit Logout succeeded\n");
2965		break;
2966	case IBMVFC_MAD_DRIVER_FAILED:
2967		kref_put(&tgt->kref, ibmvfc_release_tgt);
2968		wake_up(&vhost->work_wait_q);
2969		return;
2970	case IBMVFC_MAD_FAILED:
2971	default:
2972		tgt_err(tgt, "Implicit Logout failed: rc=0x%02X\n", status);
2973		break;
2974	};
2975
2976	if (vhost->action == IBMVFC_HOST_ACTION_TGT_INIT)
2977		ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_plogi);
2978	else if (vhost->action == IBMVFC_HOST_ACTION_QUERY_TGTS &&
2979		 tgt->scsi_id != tgt->new_scsi_id)
2980		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
2981	kref_put(&tgt->kref, ibmvfc_release_tgt);
2982	wake_up(&vhost->work_wait_q);
2983}
2984
2985/**
2986 * ibmvfc_tgt_implicit_logout - Initiate an Implicit Logout for specified target
2987 * @tgt:		ibmvfc target struct
2988 *
2989 **/
2990static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt)
2991{
2992	struct ibmvfc_implicit_logout *mad;
2993	struct ibmvfc_host *vhost = tgt->vhost;
2994	struct ibmvfc_event *evt;
2995
2996	if (vhost->discovery_threads >= disc_threads)
2997		return;
2998
2999	kref_get(&tgt->kref);
3000	evt = ibmvfc_get_event(vhost);
3001	vhost->discovery_threads++;
3002	ibmvfc_init_event(evt, ibmvfc_tgt_implicit_logout_done, IBMVFC_MAD_FORMAT);
3003	evt->tgt = tgt;
3004	mad = &evt->iu.implicit_logout;
3005	memset(mad, 0, sizeof(*mad));
3006	mad->common.version = 1;
3007	mad->common.opcode = IBMVFC_IMPLICIT_LOGOUT;
3008	mad->common.length = sizeof(*mad);
3009	mad->old_scsi_id = tgt->scsi_id;
3010
3011	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
3012	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
3013		vhost->discovery_threads--;
3014		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
3015		kref_put(&tgt->kref, ibmvfc_release_tgt);
3016	} else
3017		tgt_dbg(tgt, "Sent Implicit Logout\n");
3018}
3019
3020/**
3021 * ibmvfc_adisc_needs_plogi - Does device need PLOGI?
3022 * @mad:	ibmvfc passthru mad struct
3023 * @tgt:	ibmvfc target struct
3024 *
3025 * Returns:
3026 *	1 if PLOGI needed / 0 if PLOGI not needed
3027 **/
3028static int ibmvfc_adisc_needs_plogi(struct ibmvfc_passthru_mad *mad,
3029				    struct ibmvfc_target *tgt)
3030{
3031	if (memcmp(&mad->fc_iu.response[2], &tgt->ids.port_name,
3032		   sizeof(tgt->ids.port_name)))
3033		return 1;
3034	if (memcmp(&mad->fc_iu.response[4], &tgt->ids.node_name,
3035		   sizeof(tgt->ids.node_name)))
3036		return 1;
3037	if (mad->fc_iu.response[6] != tgt->scsi_id)
3038		return 1;
3039	return 0;
3040}
3041
3042/**
3043 * ibmvfc_tgt_adisc_done - Completion handler for ADISC
3044 * @evt:	ibmvfc event struct
3045 *
3046 **/
3047static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt)
3048{
3049	struct ibmvfc_target *tgt = evt->tgt;
3050	struct ibmvfc_host *vhost = evt->vhost;
3051	struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru;
3052	u32 status = mad->common.status;
3053	u8 fc_reason, fc_explain;
3054
3055	vhost->discovery_threads--;
3056	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
3057
3058	switch (status) {
3059	case IBMVFC_MAD_SUCCESS:
3060		tgt_dbg(tgt, "ADISC succeeded\n");
3061		if (ibmvfc_adisc_needs_plogi(mad, tgt))
3062			tgt->need_login = 1;
3063		break;
3064	case IBMVFC_MAD_DRIVER_FAILED:
3065		break;
3066	case IBMVFC_MAD_FAILED:
3067	default:
3068		tgt->need_login = 1;
3069		fc_reason = (mad->fc_iu.response[1] & 0x00ff0000) >> 16;
3070		fc_explain = (mad->fc_iu.response[1] & 0x0000ff00) >> 8;
3071		tgt_info(tgt, "ADISC failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
3072			 ibmvfc_get_cmd_error(mad->iu.status, mad->iu.error),
3073			 mad->iu.status, mad->iu.error,
3074			 ibmvfc_get_fc_type(fc_reason), fc_reason,
3075			 ibmvfc_get_ls_explain(fc_explain), fc_explain, status);
3076		break;
3077	};
3078
3079	kref_put(&tgt->kref, ibmvfc_release_tgt);
3080	ibmvfc_free_event(evt);
3081	wake_up(&vhost->work_wait_q);
3082}
3083
3084/**
3085 * ibmvfc_init_passthru - Initialize an event struct for FC passthru
3086 * @evt:		ibmvfc event struct
3087 *
3088 **/
3089static void ibmvfc_init_passthru(struct ibmvfc_event *evt)
3090{
3091	struct ibmvfc_passthru_mad *mad = &evt->iu.passthru;
3092
3093	memset(mad, 0, sizeof(*mad));
3094	mad->common.version = 1;
3095	mad->common.opcode = IBMVFC_PASSTHRU;
3096	mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu);
3097	mad->cmd_ioba.va = (u64)evt->crq.ioba +
3098		offsetof(struct ibmvfc_passthru_mad, iu);
3099	mad->cmd_ioba.len = sizeof(mad->iu);
3100	mad->iu.cmd_len = sizeof(mad->fc_iu.payload);
3101	mad->iu.rsp_len = sizeof(mad->fc_iu.response);
3102	mad->iu.cmd.va = (u64)evt->crq.ioba +
3103		offsetof(struct ibmvfc_passthru_mad, fc_iu) +
3104		offsetof(struct ibmvfc_passthru_fc_iu, payload);
3105	mad->iu.cmd.len = sizeof(mad->fc_iu.payload);
3106	mad->iu.rsp.va = (u64)evt->crq.ioba +
3107		offsetof(struct ibmvfc_passthru_mad, fc_iu) +
3108		offsetof(struct ibmvfc_passthru_fc_iu, response);
3109	mad->iu.rsp.len = sizeof(mad->fc_iu.response);
3110}
3111
3112/**
3113 * ibmvfc_tgt_adisc - Initiate an ADISC for specified target
3114 * @tgt:		ibmvfc target struct
3115 *
3116 **/
3117static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt)
3118{
3119	struct ibmvfc_passthru_mad *mad;
3120	struct ibmvfc_host *vhost = tgt->vhost;
3121	struct ibmvfc_event *evt;
3122
3123	if (vhost->discovery_threads >= disc_threads)
3124		return;
3125
3126	kref_get(&tgt->kref);
3127	evt = ibmvfc_get_event(vhost);
3128	vhost->discovery_threads++;
3129	ibmvfc_init_event(evt, ibmvfc_tgt_adisc_done, IBMVFC_MAD_FORMAT);
3130	evt->tgt = tgt;
3131
3132	ibmvfc_init_passthru(evt);
3133	mad = &evt->iu.passthru;
3134	mad->iu.flags = IBMVFC_FC_ELS;
3135	mad->iu.scsi_id = tgt->scsi_id;
3136
3137	mad->fc_iu.payload[0] = IBMVFC_ADISC;
3138	memcpy(&mad->fc_iu.payload[2], &vhost->login_buf->resp.port_name,
3139	       sizeof(vhost->login_buf->resp.port_name));
3140	memcpy(&mad->fc_iu.payload[4], &vhost->login_buf->resp.node_name,
3141	       sizeof(vhost->login_buf->resp.node_name));
3142	mad->fc_iu.payload[6] = vhost->login_buf->resp.scsi_id & 0x00ffffff;
3143
3144	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
3145	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
3146		vhost->discovery_threads--;
3147		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
3148		kref_put(&tgt->kref, ibmvfc_release_tgt);
3149	} else
3150		tgt_dbg(tgt, "Sent ADISC\n");
3151}
3152
3153/**
3154 * ibmvfc_tgt_query_target_done - Completion handler for Query Target MAD
3155 * @evt:	ibmvfc event struct
3156 *
3157 **/
3158static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
3159{
3160	struct ibmvfc_target *tgt = evt->tgt;
3161	struct ibmvfc_host *vhost = evt->vhost;
3162	struct ibmvfc_query_tgt *rsp = &evt->xfer_iu->query_tgt;
3163	u32 status = rsp->common.status;
3164
3165	vhost->discovery_threads--;
3166	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
3167	switch (status) {
3168	case IBMVFC_MAD_SUCCESS:
3169		tgt_dbg(tgt, "Query Target succeeded\n");
3170		tgt->new_scsi_id = rsp->scsi_id;
3171		if (rsp->scsi_id != tgt->scsi_id)
3172			ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
3173		else
3174			ibmvfc_init_tgt(tgt, ibmvfc_tgt_adisc);
3175		break;
3176	case IBMVFC_MAD_DRIVER_FAILED:
3177		break;
3178	case IBMVFC_MAD_CRQ_ERROR:
3179		ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
3180		break;
3181	case IBMVFC_MAD_FAILED:
3182	default:
3183		tgt_err(tgt, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
3184			ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
3185			ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
3186			ibmvfc_get_gs_explain(rsp->fc_explain), rsp->fc_explain, status);
3187
3188		if ((rsp->status & IBMVFC_FABRIC_MAPPED) == IBMVFC_FABRIC_MAPPED &&
3189		    rsp->error == IBMVFC_UNABLE_TO_PERFORM_REQ &&
3190		    rsp->fc_explain == IBMVFC_PORT_NAME_NOT_REG)
3191			ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
3192		else if (ibmvfc_retry_cmd(rsp->status, rsp->error))
3193			ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
3194		else
3195			ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
3196		break;
3197	};
3198
3199	kref_put(&tgt->kref, ibmvfc_release_tgt);
3200	ibmvfc_free_event(evt);
3201	wake_up(&vhost->work_wait_q);
3202}
3203
3204/**
3205 * ibmvfc_tgt_query_target - Initiate a Query Target for specified target
3206 * @tgt:	ibmvfc target struct
3207 *
3208 **/
3209static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
3210{
3211	struct ibmvfc_query_tgt *query_tgt;
3212	struct ibmvfc_host *vhost = tgt->vhost;
3213	struct ibmvfc_event *evt;
3214
3215	if (vhost->discovery_threads >= disc_threads)
3216		return;
3217
3218	kref_get(&tgt->kref);
3219	evt = ibmvfc_get_event(vhost);
3220	vhost->discovery_threads++;
3221	evt->tgt = tgt;
3222	ibmvfc_init_event(evt, ibmvfc_tgt_query_target_done, IBMVFC_MAD_FORMAT);
3223	query_tgt = &evt->iu.query_tgt;
3224	memset(query_tgt, 0, sizeof(*query_tgt));
3225	query_tgt->common.version = 1;
3226	query_tgt->common.opcode = IBMVFC_QUERY_TARGET;
3227	query_tgt->common.length = sizeof(*query_tgt);
3228	query_tgt->wwpn = tgt->ids.port_name;
3229
3230	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
3231	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
3232		vhost->discovery_threads--;
3233		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
3234		kref_put(&tgt->kref, ibmvfc_release_tgt);
3235	} else
3236		tgt_dbg(tgt, "Sent Query Target\n");
3237}
3238
3239/**
3240 * ibmvfc_alloc_target - Allocate and initialize an ibmvfc target
3241 * @vhost:		ibmvfc host struct
3242 * @scsi_id:	SCSI ID to allocate target for
3243 *
3244 * Returns:
3245 *	0 on success / other on failure
3246 **/
3247static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, u64 scsi_id)
3248{
3249	struct ibmvfc_target *tgt;
3250	unsigned long flags;
3251
3252	spin_lock_irqsave(vhost->host->host_lock, flags);
3253	list_for_each_entry(tgt, &vhost->targets, queue) {
3254		if (tgt->scsi_id == scsi_id) {
3255			if (tgt->need_login)
3256				ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
3257			goto unlock_out;
3258		}
3259	}
3260	spin_unlock_irqrestore(vhost->host->host_lock, flags);
3261
3262	tgt = mempool_alloc(vhost->tgt_pool, GFP_KERNEL);
3263	if (!tgt) {
3264		dev_err(vhost->dev, "Target allocation failure for scsi id %08llx\n",
3265			scsi_id);
3266		return -ENOMEM;
3267	}
3268
3269	tgt->scsi_id = scsi_id;
3270	tgt->new_scsi_id = scsi_id;
3271	tgt->vhost = vhost;
3272	tgt->need_login = 1;
3273	kref_init(&tgt->kref);
3274	ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
3275	spin_lock_irqsave(vhost->host->host_lock, flags);
3276	list_add_tail(&tgt->queue, &vhost->targets);
3277
3278unlock_out:
3279	spin_unlock_irqrestore(vhost->host->host_lock, flags);
3280	return 0;
3281}
3282
3283/**
3284 * ibmvfc_alloc_targets - Allocate and initialize ibmvfc targets
3285 * @vhost:		ibmvfc host struct
3286 *
3287 * Returns:
3288 *	0 on success / other on failure
3289 **/
3290static int ibmvfc_alloc_targets(struct ibmvfc_host *vhost)
3291{
3292	int i, rc;
3293
3294	for (i = 0, rc = 0; !rc && i < vhost->num_targets; i++)
3295		rc = ibmvfc_alloc_target(vhost,
3296					 vhost->disc_buf->scsi_id[i] & IBMVFC_DISC_TGT_SCSI_ID_MASK);
3297
3298	return rc;
3299}
3300
3301/**
3302 * ibmvfc_discover_targets_done - Completion handler for discover targets MAD
3303 * @evt:	ibmvfc event struct
3304 *
3305 **/
3306static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
3307{
3308	struct ibmvfc_host *vhost = evt->vhost;
3309	struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets;
3310	u32 mad_status = rsp->common.status;
3311
3312	switch (mad_status) {
3313	case IBMVFC_MAD_SUCCESS:
3314		ibmvfc_dbg(vhost, "Discover Targets succeeded\n");
3315		vhost->num_targets = rsp->num_written;
3316		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
3317		break;
3318	case IBMVFC_MAD_FAILED:
3319		dev_err(vhost->dev, "Discover Targets failed: %s (%x:%x)\n",
3320			ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
3321		ibmvfc_retry_host_init(vhost);
3322		break;
3323	case IBMVFC_MAD_DRIVER_FAILED:
3324		break;
3325	default:
3326		dev_err(vhost->dev, "Invalid Discover Targets response: 0x%x\n", mad_status);
3327		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
3328		break;
3329	}
3330
3331	ibmvfc_free_event(evt);
3332	wake_up(&vhost->work_wait_q);
3333}
3334
3335/**
3336 * ibmvfc_discover_targets - Send Discover Targets MAD
3337 * @vhost:	ibmvfc host struct
3338 *
3339 **/
3340static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
3341{
3342	struct ibmvfc_discover_targets *mad;
3343	struct ibmvfc_event *evt = ibmvfc_get_event(vhost);
3344
3345	ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT);
3346	mad = &evt->iu.discover_targets;
3347	memset(mad, 0, sizeof(*mad));
3348	mad->common.version = 1;
3349	mad->common.opcode = IBMVFC_DISC_TARGETS;
3350	mad->common.length = sizeof(*mad);
3351	mad->bufflen = vhost->disc_buf_sz;
3352	mad->buffer.va = vhost->disc_buf_dma;
3353	mad->buffer.len = vhost->disc_buf_sz;
3354	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
3355
3356	if (!ibmvfc_send_event(evt, vhost, default_timeout))
3357		ibmvfc_dbg(vhost, "Sent discover targets\n");
3358	else
3359		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
3360}
3361
3362/**
3363 * ibmvfc_npiv_login_done - Completion handler for NPIV Login
3364 * @evt:	ibmvfc event struct
3365 *
3366 **/
3367static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
3368{
3369	struct ibmvfc_host *vhost = evt->vhost;
3370	u32 mad_status = evt->xfer_iu->npiv_login.common.status;
3371	struct ibmvfc_npiv_login_resp *rsp = &vhost->login_buf->resp;
3372	unsigned int npiv_max_sectors;
3373
3374	switch (mad_status) {
3375	case IBMVFC_MAD_SUCCESS:
3376		ibmvfc_free_event(evt);
3377		break;
3378	case IBMVFC_MAD_FAILED:
3379		dev_err(vhost->dev, "NPIV Login failed: %s (%x:%x)\n",
3380			ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
3381		if (ibmvfc_retry_cmd(rsp->status, rsp->error))
3382			ibmvfc_retry_host_init(vhost);
3383		else
3384			ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
3385		ibmvfc_free_event(evt);
3386		return;
3387	case IBMVFC_MAD_CRQ_ERROR:
3388		ibmvfc_retry_host_init(vhost);
3389	case IBMVFC_MAD_DRIVER_FAILED:
3390		ibmvfc_free_event(evt);
3391		return;
3392	default:
3393		dev_err(vhost->dev, "Invalid NPIV Login response: 0x%x\n", mad_status);
3394		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
3395		ibmvfc_free_event(evt);
3396		return;
3397	}
3398
3399	vhost->client_migrated = 0;
3400
3401	if (!(rsp->flags & IBMVFC_NATIVE_FC)) {
3402		dev_err(vhost->dev, "Virtual adapter does not support FC. %x\n",
3403			rsp->flags);
3404		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
3405		wake_up(&vhost->work_wait_q);
3406		return;
3407	}
3408
3409	if (rsp->max_cmds <= IBMVFC_NUM_INTERNAL_REQ) {
3410		dev_err(vhost->dev, "Virtual adapter supported queue depth too small: %d\n",
3411			rsp->max_cmds);
3412		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
3413		wake_up(&vhost->work_wait_q);
3414		return;
3415	}
3416
3417	npiv_max_sectors = min((uint)(rsp->max_dma_len >> 9), IBMVFC_MAX_SECTORS);
3418	dev_info(vhost->dev, "Host partition: %s, device: %s %s %s max sectors %u\n",
3419		 rsp->partition_name, rsp->device_name, rsp->port_loc_code,
3420		 rsp->drc_name, npiv_max_sectors);
3421
3422	fc_host_fabric_name(vhost->host) = rsp->node_name;
3423	fc_host_node_name(vhost->host) = rsp->node_name;
3424	fc_host_port_name(vhost->host) = rsp->port_name;
3425	fc_host_port_id(vhost->host) = rsp->scsi_id;
3426	fc_host_port_type(vhost->host) = FC_PORTTYPE_NPIV;
3427	fc_host_supported_classes(vhost->host) = 0;
3428	if (rsp->service_parms.class1_parms[0] & 0x80000000)
3429		fc_host_supported_classes(vhost->host) |= FC_COS_CLASS1;
3430	if (rsp->service_parms.class2_parms[0] & 0x80000000)
3431		fc_host_supported_classes(vhost->host) |= FC_COS_CLASS2;
3432	if (rsp->service_parms.class3_parms[0] & 0x80000000)
3433		fc_host_supported_classes(vhost->host) |= FC_COS_CLASS3;
3434	fc_host_maxframe_size(vhost->host) =
3435		rsp->service_parms.common.bb_rcv_sz & 0x0fff;
3436
3437	vhost->host->can_queue = rsp->max_cmds - IBMVFC_NUM_INTERNAL_REQ;
3438	vhost->host->max_sectors = npiv_max_sectors;
3439	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
3440	wake_up(&vhost->work_wait_q);
3441}
3442
3443/**
3444 * ibmvfc_npiv_login - Sends NPIV login
3445 * @vhost:	ibmvfc host struct
3446 *
3447 **/
3448static void ibmvfc_npiv_login(struct ibmvfc_host *vhost)
3449{
3450	struct ibmvfc_npiv_login_mad *mad;
3451	struct ibmvfc_event *evt = ibmvfc_get_event(vhost);
3452
3453	ibmvfc_gather_partition_info(vhost);
3454	ibmvfc_set_login_info(vhost);
3455	ibmvfc_init_event(evt, ibmvfc_npiv_login_done, IBMVFC_MAD_FORMAT);
3456
3457	memcpy(vhost->login_buf, &vhost->login_info, sizeof(vhost->login_info));
3458	mad = &evt->iu.npiv_login;
3459	memset(mad, 0, sizeof(struct ibmvfc_npiv_login_mad));
3460	mad->common.version = 1;
3461	mad->common.opcode = IBMVFC_NPIV_LOGIN;
3462	mad->common.length = sizeof(struct ibmvfc_npiv_login_mad);
3463	mad->buffer.va = vhost->login_buf_dma;
3464	mad->buffer.len = sizeof(*vhost->login_buf);
3465
3466	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
3467
3468	if (!ibmvfc_send_event(evt, vhost, default_timeout))
3469		ibmvfc_dbg(vhost, "Sent NPIV login\n");
3470	else
3471		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
3472};
3473
3474/**
3475 * ibmvfc_dev_init_to_do - Is there target initialization work to do?
3476 * @vhost:		ibmvfc host struct
3477 *
3478 * Returns:
3479 *	1 if work to do / 0 if not
3480 **/
3481static int ibmvfc_dev_init_to_do(struct ibmvfc_host *vhost)
3482{
3483	struct ibmvfc_target *tgt;
3484
3485	list_for_each_entry(tgt, &vhost->targets, queue) {
3486		if (tgt->action == IBMVFC_TGT_ACTION_INIT ||
3487		    tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
3488			return 1;
3489	}
3490
3491	return 0;
3492}
3493
3494/**
3495 * __ibmvfc_work_to_do - Is there task level work to do? (no locking)
3496 * @vhost:		ibmvfc host struct
3497 *
3498 * Returns:
3499 *	1 if work to do / 0 if not
3500 **/
3501static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
3502{
3503	struct ibmvfc_target *tgt;
3504
3505	if (kthread_should_stop())
3506		return 1;
3507	switch (vhost->action) {
3508	case IBMVFC_HOST_ACTION_NONE:
3509	case IBMVFC_HOST_ACTION_INIT_WAIT:
3510		return 0;
3511	case IBMVFC_HOST_ACTION_TGT_INIT:
3512	case IBMVFC_HOST_ACTION_QUERY_TGTS:
3513		if (vhost->discovery_threads == disc_threads)
3514			return 0;
3515		list_for_each_entry(tgt, &vhost->targets, queue)
3516			if (tgt->action == IBMVFC_TGT_ACTION_INIT)
3517				return 1;
3518		list_for_each_entry(tgt, &vhost->targets, queue)
3519			if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
3520				return 0;
3521		return 1;
3522	case IBMVFC_HOST_ACTION_INIT:
3523	case IBMVFC_HOST_ACTION_ALLOC_TGTS:
3524	case IBMVFC_HOST_ACTION_TGT_ADD:
3525	case IBMVFC_HOST_ACTION_TGT_DEL:
3526	case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
3527	case IBMVFC_HOST_ACTION_QUERY:
3528	default:
3529		break;
3530	};
3531
3532	return 1;
3533}
3534
3535/**
3536 * ibmvfc_work_to_do - Is there task level work to do?
3537 * @vhost:		ibmvfc host struct
3538 *
3539 * Returns:
3540 *	1 if work to do / 0 if not
3541 **/
3542static int ibmvfc_work_to_do(struct ibmvfc_host *vhost)
3543{
3544	unsigned long flags;
3545	int rc;
3546
3547	spin_lock_irqsave(vhost->host->host_lock, flags);
3548	rc = __ibmvfc_work_to_do(vhost);
3549	spin_unlock_irqrestore(vhost->host->host_lock, flags);
3550	return rc;
3551}
3552
3553/**
3554 * ibmvfc_log_ae - Log async events if necessary
3555 * @vhost:		ibmvfc host struct
3556 * @events:		events to log
3557 *
3558 **/
3559static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events)
3560{
3561	if (events & IBMVFC_AE_RSCN)
3562		fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_RSCN, 0);
3563	if ((events & IBMVFC_AE_LINKDOWN) &&
3564	    vhost->state >= IBMVFC_HALTED)
3565		fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_LINKDOWN, 0);
3566	if ((events & IBMVFC_AE_LINKUP) &&
3567	    vhost->state == IBMVFC_INITIALIZING)
3568		fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_LINKUP, 0);
3569}
3570
3571/**
3572 * ibmvfc_tgt_add_rport - Tell the FC transport about a new remote port
3573 * @tgt:		ibmvfc target struct
3574 *
3575 **/
3576static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
3577{
3578	struct ibmvfc_host *vhost = tgt->vhost;
3579	struct fc_rport *rport;
3580	unsigned long flags;
3581
3582	tgt_dbg(tgt, "Adding rport\n");
3583	rport = fc_remote_port_add(vhost->host, 0, &tgt->ids);
3584	spin_lock_irqsave(vhost->host->host_lock, flags);
3585	tgt->rport = rport;
3586	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
3587	if (rport) {
3588		tgt_dbg(tgt, "rport add succeeded\n");
3589		rport->maxframe_size = tgt->service_parms.common.bb_rcv_sz & 0x0fff;
3590		rport->supported_classes = 0;
3591		tgt->target_id = rport->scsi_target_id;
3592		if (tgt->service_parms.class1_parms[0] & 0x80000000)
3593			rport->supported_classes |= FC_COS_CLASS1;
3594		if (tgt->service_parms.class2_parms[0] & 0x80000000)
3595			rport->supported_classes |= FC_COS_CLASS2;
3596		if (tgt->service_parms.class3_parms[0] & 0x80000000)
3597			rport->supported_classes |= FC_COS_CLASS3;
3598	} else
3599		tgt_dbg(tgt, "rport add failed\n");
3600	spin_unlock_irqrestore(vhost->host->host_lock, flags);
3601}
3602
3603/**
3604 * ibmvfc_do_work - Do task level work
3605 * @vhost:		ibmvfc host struct
3606 *
3607 **/
3608static void ibmvfc_do_work(struct ibmvfc_host *vhost)
3609{
3610	struct ibmvfc_target *tgt;
3611	unsigned long flags;
3612	struct fc_rport *rport;
3613
3614	ibmvfc_log_ae(vhost, vhost->events_to_log);
3615	spin_lock_irqsave(vhost->host->host_lock, flags);
3616	vhost->events_to_log = 0;
3617	switch (vhost->action) {
3618	case IBMVFC_HOST_ACTION_NONE:
3619	case IBMVFC_HOST_ACTION_INIT_WAIT:
3620		break;
3621	case IBMVFC_HOST_ACTION_INIT:
3622		BUG_ON(vhost->state != IBMVFC_INITIALIZING);
3623		if (vhost->delay_init) {
3624			vhost->delay_init = 0;
3625			spin_unlock_irqrestore(vhost->host->host_lock, flags);
3626			ssleep(15);
3627			return;
3628		} else
3629			vhost->job_step(vhost);
3630		break;
3631	case IBMVFC_HOST_ACTION_QUERY:
3632		list_for_each_entry(tgt, &vhost->targets, queue)
3633			ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target);
3634		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY_TGTS);
3635		break;
3636	case IBMVFC_HOST_ACTION_QUERY_TGTS:
3637		list_for_each_entry(tgt, &vhost->targets, queue) {
3638			if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
3639				tgt->job_step(tgt);
3640				break;
3641			}
3642		}
3643
3644		if (!ibmvfc_dev_init_to_do(vhost))
3645			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
3646		break;
3647	case IBMVFC_HOST_ACTION_TGT_DEL:
3648	case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
3649		list_for_each_entry(tgt, &vhost->targets, queue) {
3650			if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
3651				tgt_dbg(tgt, "Deleting rport\n");
3652				rport = tgt->rport;
3653				tgt->rport = NULL;
3654				list_del(&tgt->queue);
3655				spin_unlock_irqrestore(vhost->host->host_lock, flags);
3656				if (rport)
3657					fc_remote_port_delete(rport);
3658				kref_put(&tgt->kref, ibmvfc_release_tgt);
3659				return;
3660			}
3661		}
3662
3663		if (vhost->state == IBMVFC_INITIALIZING) {
3664			if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) {
3665				ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
3666				ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_ADD);
3667				vhost->init_retries = 0;
3668				spin_unlock_irqrestore(vhost->host->host_lock, flags);
3669				scsi_unblock_requests(vhost->host);
3670				return;
3671			} else {
3672				ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
3673				vhost->job_step = ibmvfc_discover_targets;
3674			}
3675		} else {
3676			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
3677			spin_unlock_irqrestore(vhost->host->host_lock, flags);
3678			scsi_unblock_requests(vhost->host);
3679			wake_up(&vhost->init_wait_q);
3680			return;
3681		}
3682		break;
3683	case IBMVFC_HOST_ACTION_ALLOC_TGTS:
3684		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_INIT);
3685		spin_unlock_irqrestore(vhost->host->host_lock, flags);
3686		ibmvfc_alloc_targets(vhost);
3687		spin_lock_irqsave(vhost->host->host_lock, flags);
3688		break;
3689	case IBMVFC_HOST_ACTION_TGT_INIT:
3690		list_for_each_entry(tgt, &vhost->targets, queue) {
3691			if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
3692				tgt->job_step(tgt);
3693				break;
3694			}
3695		}
3696
3697		if (!ibmvfc_dev_init_to_do(vhost))
3698			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL_FAILED);
3699		break;
3700	case IBMVFC_HOST_ACTION_TGT_ADD:
3701		list_for_each_entry(tgt, &vhost->targets, queue) {
3702			if (tgt->action == IBMVFC_TGT_ACTION_ADD_RPORT) {
3703				spin_unlock_irqrestore(vhost->host->host_lock, flags);
3704				ibmvfc_tgt_add_rport(tgt);
3705				return;
3706			}
3707		}
3708
3709		if (vhost->reinit && !ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
3710			vhost->reinit = 0;
3711			scsi_block_requests(vhost->host);
3712			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
3713		} else {
3714			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
3715			wake_up(&vhost->init_wait_q);
3716		}
3717		break;
3718	default:
3719		break;
3720	};
3721
3722	spin_unlock_irqrestore(vhost->host->host_lock, flags);
3723}
3724
3725/**
3726 * ibmvfc_work - Do task level work
3727 * @data:		ibmvfc host struct
3728 *
3729 * Returns:
3730 *	zero
3731 **/
3732static int ibmvfc_work(void *data)
3733{
3734	struct ibmvfc_host *vhost = data;
3735	int rc;
3736
3737	set_user_nice(current, -20);
3738
3739	while (1) {
3740		rc = wait_event_interruptible(vhost->work_wait_q,
3741					      ibmvfc_work_to_do(vhost));
3742
3743		BUG_ON(rc);
3744
3745		if (kthread_should_stop())
3746			break;
3747
3748		ibmvfc_do_work(vhost);
3749	}
3750
3751	ibmvfc_dbg(vhost, "ibmvfc kthread exiting...\n");
3752	return 0;
3753}
3754
3755/**
3756 * ibmvfc_init_crq - Initializes and registers CRQ with hypervisor
3757 * @vhost:	ibmvfc host struct
3758 *
3759 * Allocates a page for messages, maps it for dma, and registers
3760 * the crq with the hypervisor.
3761 *
3762 * Return value:
3763 *	zero on success / other on failure
3764 **/
3765static int ibmvfc_init_crq(struct ibmvfc_host *vhost)
3766{
3767	int rc, retrc = -ENOMEM;
3768	struct device *dev = vhost->dev;
3769	struct vio_dev *vdev = to_vio_dev(dev);
3770	struct ibmvfc_crq_queue *crq = &vhost->crq;
3771
3772	ENTER;
3773	crq->msgs = (struct ibmvfc_crq *)get_zeroed_page(GFP_KERNEL);
3774
3775	if (!crq->msgs)
3776		return -ENOMEM;
3777
3778	crq->size = PAGE_SIZE / sizeof(*crq->msgs);
3779	crq->msg_token = dma_map_single(dev, crq->msgs,
3780					PAGE_SIZE, DMA_BIDIRECTIONAL);
3781
3782	if (dma_mapping_error(dev, crq->msg_token))
3783		goto map_failed;
3784
3785	retrc = rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
3786					crq->msg_token, PAGE_SIZE);
3787
3788	if (rc == H_RESOURCE)
3789		/* maybe kexecing and resource is busy. try a reset */
3790		retrc = rc = ibmvfc_reset_crq(vhost);
3791
3792	if (rc == H_CLOSED)
3793		dev_warn(dev, "Partner adapter not ready\n");
3794	else if (rc) {
3795		dev_warn(dev, "Error %d opening adapter\n", rc);
3796		goto reg_crq_failed;
3797	}
3798
3799	retrc = 0;
3800
3801	if ((rc = request_irq(vdev->irq, ibmvfc_interrupt, 0, IBMVFC_NAME, vhost))) {
3802		dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", vdev->irq, rc);
3803		goto req_irq_failed;
3804	}
3805
3806	if ((rc = vio_enable_interrupts(vdev))) {
3807		dev_err(dev, "Error %d enabling interrupts\n", rc);
3808		goto req_irq_failed;
3809	}
3810
3811	crq->cur = 0;
3812	LEAVE;
3813	return retrc;
3814
3815req_irq_failed:
3816	do {
3817		rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
3818	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
3819reg_crq_failed:
3820	dma_unmap_single(dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
3821map_failed:
3822	free_page((unsigned long)crq->msgs);
3823	return retrc;
3824}
3825
3826/**
3827 * ibmvfc_free_mem - Free memory for vhost
3828 * @vhost:	ibmvfc host struct
3829 *
3830 * Return value:
3831 * 	none
3832 **/
3833static void ibmvfc_free_mem(struct ibmvfc_host *vhost)
3834{
3835	struct ibmvfc_async_crq_queue *async_q = &vhost->async_crq;
3836
3837	ENTER;
3838	mempool_destroy(vhost->tgt_pool);
3839	kfree(vhost->trace);
3840	dma_free_coherent(vhost->dev, vhost->disc_buf_sz, vhost->disc_buf,
3841			  vhost->disc_buf_dma);
3842	dma_free_coherent(vhost->dev, sizeof(*vhost->login_buf),
3843			  vhost->login_buf, vhost->login_buf_dma);
3844	dma_pool_destroy(vhost->sg_pool);
3845	dma_unmap_single(vhost->dev, async_q->msg_token,
3846			 async_q->size * sizeof(*async_q->msgs), DMA_BIDIRECTIONAL);
3847	free_page((unsigned long)async_q->msgs);
3848	LEAVE;
3849}
3850
3851/**
3852 * ibmvfc_alloc_mem - Allocate memory for vhost
3853 * @vhost:	ibmvfc host struct
3854 *
3855 * Return value:
3856 * 	0 on success / non-zero on failure
3857 **/
3858static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
3859{
3860	struct ibmvfc_async_crq_queue *async_q = &vhost->async_crq;
3861	struct device *dev = vhost->dev;
3862
3863	ENTER;
3864	async_q->msgs = (struct ibmvfc_async_crq *)get_zeroed_page(GFP_KERNEL);
3865	if (!async_q->msgs) {
3866		dev_err(dev, "Couldn't allocate async queue.\n");
3867		goto nomem;
3868	}
3869
3870	async_q->size = PAGE_SIZE / sizeof(struct ibmvfc_async_crq);
3871	async_q->msg_token = dma_map_single(dev, async_q->msgs,
3872					    async_q->size * sizeof(*async_q->msgs),
3873					    DMA_BIDIRECTIONAL);
3874
3875	if (dma_mapping_error(dev, async_q->msg_token)) {
3876		dev_err(dev, "Failed to map async queue\n");
3877		goto free_async_crq;
3878	}
3879
3880	vhost->sg_pool = dma_pool_create(IBMVFC_NAME, dev,
3881					 SG_ALL * sizeof(struct srp_direct_buf),
3882					 sizeof(struct srp_direct_buf), 0);
3883
3884	if (!vhost->sg_pool) {
3885		dev_err(dev, "Failed to allocate sg pool\n");
3886		goto unmap_async_crq;
3887	}
3888
3889	vhost->login_buf = dma_alloc_coherent(dev, sizeof(*vhost->login_buf),
3890					      &vhost->login_buf_dma, GFP_KERNEL);
3891
3892	if (!vhost->login_buf) {
3893		dev_err(dev, "Couldn't allocate NPIV login buffer\n");
3894		goto free_sg_pool;
3895	}
3896
3897	vhost->disc_buf_sz = sizeof(vhost->disc_buf->scsi_id[0]) * max_targets;
3898	vhost->disc_buf = dma_alloc_coherent(dev, vhost->disc_buf_sz,
3899					     &vhost->disc_buf_dma, GFP_KERNEL);
3900
3901	if (!vhost->disc_buf) {
3902		dev_err(dev, "Couldn't allocate Discover Targets buffer\n");
3903		goto free_login_buffer;
3904	}
3905
3906	vhost->trace = kcalloc(IBMVFC_NUM_TRACE_ENTRIES,
3907			       sizeof(struct ibmvfc_trace_entry), GFP_KERNEL);
3908
3909	if (!vhost->trace)
3910		goto free_disc_buffer;
3911
3912	vhost->tgt_pool = mempool_create_kzalloc_pool(IBMVFC_TGT_MEMPOOL_SZ,
3913						      sizeof(struct ibmvfc_target));
3914
3915	if (!vhost->tgt_pool) {
3916		dev_err(dev, "Couldn't allocate target memory pool\n");
3917		goto free_trace;
3918	}
3919
3920	LEAVE;
3921	return 0;
3922
3923free_trace:
3924	kfree(vhost->trace);
3925free_disc_buffer:
3926	dma_free_coherent(dev, vhost->disc_buf_sz, vhost->disc_buf,
3927			  vhost->disc_buf_dma);
3928free_login_buffer:
3929	dma_free_coherent(dev, sizeof(*vhost->login_buf),
3930			  vhost->login_buf, vhost->login_buf_dma);
3931free_sg_pool:
3932	dma_pool_destroy(vhost->sg_pool);
3933unmap_async_crq:
3934	dma_unmap_single(dev, async_q->msg_token,
3935			 async_q->size * sizeof(*async_q->msgs), DMA_BIDIRECTIONAL);
3936free_async_crq:
3937	free_page((unsigned long)async_q->msgs);
3938nomem:
3939	LEAVE;
3940	return -ENOMEM;
3941}
3942
3943/**
3944 * ibmvfc_probe - Adapter hot plug add entry point
3945 * @vdev:	vio device struct
3946 * @id:	vio device id struct
3947 *
3948 * Return value:
3949 * 	0 on success / non-zero on failure
3950 **/
3951static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
3952{
3953	struct ibmvfc_host *vhost;
3954	struct Scsi_Host *shost;
3955	struct device *dev = &vdev->dev;
3956	int rc = -ENOMEM;
3957
3958	ENTER;
3959	shost = scsi_host_alloc(&driver_template, sizeof(*vhost));
3960	if (!shost) {
3961		dev_err(dev, "Couldn't allocate host data\n");
3962		goto out;
3963	}
3964
3965	shost->transportt = ibmvfc_transport_template;
3966	shost->can_queue = max_requests;
3967	shost->max_lun = max_lun;
3968	shost->max_id = max_targets;
3969	shost->max_sectors = IBMVFC_MAX_SECTORS;
3970	shost->max_cmd_len = IBMVFC_MAX_CDB_LEN;
3971	shost->unique_id = shost->host_no;
3972
3973	vhost = shost_priv(shost);
3974	INIT_LIST_HEAD(&vhost->sent);
3975	INIT_LIST_HEAD(&vhost->free);
3976	INIT_LIST_HEAD(&vhost->targets);
3977	sprintf(vhost->name, IBMVFC_NAME);
3978	vhost->host = shost;
3979	vhost->dev = dev;
3980	vhost->partition_number = -1;
3981	vhost->log_level = log_level;
3982	strcpy(vhost->partition_name, "UNKNOWN");
3983	init_waitqueue_head(&vhost->work_wait_q);
3984	init_waitqueue_head(&vhost->init_wait_q);
3985
3986	if ((rc = ibmvfc_alloc_mem(vhost)))
3987		goto free_scsi_host;
3988
3989	vhost->work_thread = kthread_run(ibmvfc_work, vhost, "%s_%d", IBMVFC_NAME,
3990					 shost->host_no);
3991
3992	if (IS_ERR(vhost->work_thread)) {
3993		dev_err(dev, "Couldn't create kernel thread: %ld\n",
3994			PTR_ERR(vhost->work_thread));
3995		goto free_host_mem;
3996	}
3997
3998	if ((rc = ibmvfc_init_crq(vhost))) {
3999		dev_err(dev, "Couldn't initialize crq. rc=%d\n", rc);
4000		goto kill_kthread;
4001	}
4002
4003	if ((rc = ibmvfc_init_event_pool(vhost))) {
4004		dev_err(dev, "Couldn't initialize event pool. rc=%d\n", rc);
4005		goto release_crq;
4006	}
4007
4008	if ((rc = scsi_add_host(shost, dev)))
4009		goto release_event_pool;
4010
4011	if ((rc = ibmvfc_create_trace_file(&shost->shost_dev.kobj,
4012					   &ibmvfc_trace_attr))) {
4013		dev_err(dev, "Failed to create trace file. rc=%d\n", rc);
4014		goto remove_shost;
4015	}
4016
4017	dev_set_drvdata(dev, vhost);
4018	spin_lock(&ibmvfc_driver_lock);
4019	list_add_tail(&vhost->queue, &ibmvfc_head);
4020	spin_unlock(&ibmvfc_driver_lock);
4021
4022	ibmvfc_send_crq_init(vhost);
4023	scsi_scan_host(shost);
4024	return 0;
4025
4026remove_shost:
4027	scsi_remove_host(shost);
4028release_event_pool:
4029	ibmvfc_free_event_pool(vhost);
4030release_crq:
4031	ibmvfc_release_crq_queue(vhost);
4032kill_kthread:
4033	kthread_stop(vhost->work_thread);
4034free_host_mem:
4035	ibmvfc_free_mem(vhost);
4036free_scsi_host:
4037	scsi_host_put(shost);
4038out:
4039	LEAVE;
4040	return rc;
4041}
4042
4043/**
4044 * ibmvfc_remove - Adapter hot plug remove entry point
4045 * @vdev:	vio device struct
4046 *
4047 * Return value:
4048 * 	0
4049 **/
4050static int ibmvfc_remove(struct vio_dev *vdev)
4051{
4052	struct ibmvfc_host *vhost = dev_get_drvdata(&vdev->dev);
4053	unsigned long flags;
4054
4055	ENTER;
4056	ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr);
4057	ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
4058	ibmvfc_wait_while_resetting(vhost);
4059	ibmvfc_release_crq_queue(vhost);
4060	kthread_stop(vhost->work_thread);
4061	fc_remove_host(vhost->host);
4062	scsi_remove_host(vhost->host);
4063
4064	spin_lock_irqsave(vhost->host->host_lock, flags);
4065	ibmvfc_purge_requests(vhost, DID_ERROR);
4066	ibmvfc_free_event_pool(vhost);
4067	spin_unlock_irqrestore(vhost->host->host_lock, flags);
4068
4069	ibmvfc_free_mem(vhost);
4070	spin_lock(&ibmvfc_driver_lock);
4071	list_del(&vhost->queue);
4072	spin_unlock(&ibmvfc_driver_lock);
4073	scsi_host_put(vhost->host);
4074	LEAVE;
4075	return 0;
4076}
4077
4078/**
4079 * ibmvfc_get_desired_dma - Calculate DMA resources needed by the driver
4080 * @vdev:	vio device struct
4081 *
4082 * Return value:
4083 *	Number of bytes the driver will need to DMA map at the same time in
4084 *	order to perform well.
4085 */
4086static unsigned long ibmvfc_get_desired_dma(struct vio_dev *vdev)
4087{
4088	unsigned long pool_dma = max_requests * sizeof(union ibmvfc_iu);
4089	return pool_dma + ((512 * 1024) * driver_template.cmd_per_lun);
4090}
4091
4092static struct vio_device_id ibmvfc_device_table[] __devinitdata = {
4093	{"fcp", "IBM,vfc-client"},
4094	{ "", "" }
4095};
4096MODULE_DEVICE_TABLE(vio, ibmvfc_device_table);
4097
4098static struct vio_driver ibmvfc_driver = {
4099	.id_table = ibmvfc_device_table,
4100	.probe = ibmvfc_probe,
4101	.remove = ibmvfc_remove,
4102	.get_desired_dma = ibmvfc_get_desired_dma,
4103	.driver = {
4104		.name = IBMVFC_NAME,
4105		.owner = THIS_MODULE,
4106	}
4107};
4108
4109static struct fc_function_template ibmvfc_transport_functions = {
4110	.show_host_fabric_name = 1,
4111	.show_host_node_name = 1,
4112	.show_host_port_name = 1,
4113	.show_host_supported_classes = 1,
4114	.show_host_port_type = 1,
4115	.show_host_port_id = 1,
4116
4117	.get_host_port_state = ibmvfc_get_host_port_state,
4118	.show_host_port_state = 1,
4119
4120	.get_host_speed = ibmvfc_get_host_speed,
4121	.show_host_speed = 1,
4122
4123	.issue_fc_host_lip = ibmvfc_issue_fc_host_lip,
4124	.terminate_rport_io = ibmvfc_terminate_rport_io,
4125
4126	.show_rport_maxframe_size = 1,
4127	.show_rport_supported_classes = 1,
4128
4129	.set_rport_dev_loss_tmo = ibmvfc_set_rport_dev_loss_tmo,
4130	.show_rport_dev_loss_tmo = 1,
4131
4132	.get_starget_node_name = ibmvfc_get_starget_node_name,
4133	.show_starget_node_name = 1,
4134
4135	.get_starget_port_name = ibmvfc_get_starget_port_name,
4136	.show_starget_port_name = 1,
4137
4138	.get_starget_port_id = ibmvfc_get_starget_port_id,
4139	.show_starget_port_id = 1,
4140};
4141
4142/**
4143 * ibmvfc_module_init - Initialize the ibmvfc module
4144 *
4145 * Return value:
4146 * 	0 on success / other on failure
4147 **/
4148static int __init ibmvfc_module_init(void)
4149{
4150	int rc;
4151
4152	if (!firmware_has_feature(FW_FEATURE_VIO))
4153		return -ENODEV;
4154
4155	printk(KERN_INFO IBMVFC_NAME": IBM Virtual Fibre Channel Driver version: %s %s\n",
4156	       IBMVFC_DRIVER_VERSION, IBMVFC_DRIVER_DATE);
4157
4158	ibmvfc_transport_template = fc_attach_transport(&ibmvfc_transport_functions);
4159	if (!ibmvfc_transport_template)
4160		return -ENOMEM;
4161
4162	rc = vio_register_driver(&ibmvfc_driver);
4163	if (rc)
4164		fc_release_transport(ibmvfc_transport_template);
4165	return rc;
4166}
4167
4168/**
4169 * ibmvfc_module_exit - Teardown the ibmvfc module
4170 *
4171 * Return value:
4172 * 	nothing
4173 **/
4174static void __exit ibmvfc_module_exit(void)
4175{
4176	vio_unregister_driver(&ibmvfc_driver);
4177	fc_release_transport(ibmvfc_transport_template);
4178}
4179
4180module_init(ibmvfc_module_init);
4181module_exit(ibmvfc_module_exit);
4182