162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*******************************************************************************
362306a36Sopenharmony_ci * IBM Virtual SCSI Target Driver
462306a36Sopenharmony_ci * Copyright (C) 2003-2005 Dave Boutcher (boutcher@us.ibm.com) IBM Corp.
562306a36Sopenharmony_ci *			   Santiago Leon (santil@us.ibm.com) IBM Corp.
662306a36Sopenharmony_ci *			   Linda Xie (lxie@us.ibm.com) IBM Corp.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2005-2011 FUJITA Tomonori <tomof@acm.org>
962306a36Sopenharmony_ci * Copyright (C) 2010 Nicholas A. Bellinger <nab@kernel.org>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Authors: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
1262306a36Sopenharmony_ci * Authors: Michael Cyr <mikecyr@linux.vnet.ibm.com>
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci ****************************************************************************/
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/kernel.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/types.h>
2262306a36Sopenharmony_ci#include <linux/list.h>
2362306a36Sopenharmony_ci#include <linux/string.h>
2462306a36Sopenharmony_ci#include <linux/delay.h>
2562306a36Sopenharmony_ci#include <linux/of.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <target/target_core_base.h>
2862306a36Sopenharmony_ci#include <target/target_core_fabric.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <asm/hvcall.h>
3162306a36Sopenharmony_ci#include <asm/vio.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <scsi/viosrp.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include "ibmvscsi_tgt.h"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define IBMVSCSIS_VERSION	"v0.2"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define	INITIAL_SRP_LIMIT	1024
4062306a36Sopenharmony_ci#define	DEFAULT_MAX_SECTORS	256
4162306a36Sopenharmony_ci#define MAX_TXU			1024 * 1024
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic uint max_vdma_size = MAX_H_COPY_RDMA;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic char system_id[SYS_ID_NAME_LEN] = "";
4662306a36Sopenharmony_cistatic char partition_name[PARTITION_NAMELEN] = "UNKNOWN";
4762306a36Sopenharmony_cistatic uint partition_number = -1;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Adapter list and lock to control it */
5062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ibmvscsis_dev_lock);
5162306a36Sopenharmony_cistatic LIST_HEAD(ibmvscsis_dev_list);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic long ibmvscsis_parse_command(struct scsi_info *vscsi,
5462306a36Sopenharmony_ci				    struct viosrp_crq *crq);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void ibmvscsis_adapter_idle(struct scsi_info *vscsi);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic void ibmvscsis_determine_resid(struct se_cmd *se_cmd,
5962306a36Sopenharmony_ci				      struct srp_rsp *rsp)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	u32 residual_count = se_cmd->residual_count;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	if (!residual_count)
6462306a36Sopenharmony_ci		return;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
6762306a36Sopenharmony_ci		if (se_cmd->data_direction == DMA_TO_DEVICE) {
6862306a36Sopenharmony_ci			/* residual data from an underflow write */
6962306a36Sopenharmony_ci			rsp->flags = SRP_RSP_FLAG_DOUNDER;
7062306a36Sopenharmony_ci			rsp->data_out_res_cnt = cpu_to_be32(residual_count);
7162306a36Sopenharmony_ci		} else if (se_cmd->data_direction == DMA_FROM_DEVICE) {
7262306a36Sopenharmony_ci			/* residual data from an underflow read */
7362306a36Sopenharmony_ci			rsp->flags = SRP_RSP_FLAG_DIUNDER;
7462306a36Sopenharmony_ci			rsp->data_in_res_cnt = cpu_to_be32(residual_count);
7562306a36Sopenharmony_ci		}
7662306a36Sopenharmony_ci	} else if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) {
7762306a36Sopenharmony_ci		if (se_cmd->data_direction == DMA_TO_DEVICE) {
7862306a36Sopenharmony_ci			/* residual data from an overflow write */
7962306a36Sopenharmony_ci			rsp->flags = SRP_RSP_FLAG_DOOVER;
8062306a36Sopenharmony_ci			rsp->data_out_res_cnt = cpu_to_be32(residual_count);
8162306a36Sopenharmony_ci		} else if (se_cmd->data_direction == DMA_FROM_DEVICE) {
8262306a36Sopenharmony_ci			/* residual data from an overflow read */
8362306a36Sopenharmony_ci			rsp->flags = SRP_RSP_FLAG_DIOVER;
8462306a36Sopenharmony_ci			rsp->data_in_res_cnt = cpu_to_be32(residual_count);
8562306a36Sopenharmony_ci		}
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/**
9062306a36Sopenharmony_ci * connection_broken() - Determine if the connection to the client is good
9162306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
9262306a36Sopenharmony_ci *
9362306a36Sopenharmony_ci * This function attempts to send a ping MAD to the client. If the call to
9462306a36Sopenharmony_ci * queue the request returns H_CLOSED then the connection has been broken
9562306a36Sopenharmony_ci * and the function returns TRUE.
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
9862306a36Sopenharmony_ci *	Interrupt or Process environment
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_cistatic bool connection_broken(struct scsi_info *vscsi)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct viosrp_crq *crq;
10362306a36Sopenharmony_ci	u64 buffer[2] = { 0, 0 };
10462306a36Sopenharmony_ci	long h_return_code;
10562306a36Sopenharmony_ci	bool rc = false;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* create a PING crq */
10862306a36Sopenharmony_ci	crq = (struct viosrp_crq *)&buffer;
10962306a36Sopenharmony_ci	crq->valid = VALID_CMD_RESP_EL;
11062306a36Sopenharmony_ci	crq->format = MESSAGE_IN_CRQ;
11162306a36Sopenharmony_ci	crq->status = PING;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	h_return_code = h_send_crq(vscsi->dds.unit_id,
11462306a36Sopenharmony_ci				   cpu_to_be64(buffer[MSG_HI]),
11562306a36Sopenharmony_ci				   cpu_to_be64(buffer[MSG_LOW]));
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "Connection_broken: rc %ld\n", h_return_code);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (h_return_code == H_CLOSED)
12062306a36Sopenharmony_ci		rc = true;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return rc;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/**
12662306a36Sopenharmony_ci * ibmvscsis_unregister_command_q() - Helper Function-Unregister Command Queue
12762306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * This function calls h_free_q then frees the interrupt bit etc.
13062306a36Sopenharmony_ci * It must release the lock before doing so because of the time it can take
13162306a36Sopenharmony_ci * for h_free_crq in PHYP
13262306a36Sopenharmony_ci * NOTE: * the caller must make sure that state and or flags will prevent
13362306a36Sopenharmony_ci *	   interrupt handler from scheduling work.
13462306a36Sopenharmony_ci *       * anyone calling this function may need to set the CRQ_CLOSED flag
13562306a36Sopenharmony_ci *	   we can't do it here, because we don't have the lock
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
13862306a36Sopenharmony_ci *	Process level
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_cistatic long ibmvscsis_unregister_command_q(struct scsi_info *vscsi)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	long qrc;
14362306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
14462306a36Sopenharmony_ci	int ticks = 0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	do {
14762306a36Sopenharmony_ci		qrc = h_free_crq(vscsi->dds.unit_id);
14862306a36Sopenharmony_ci		switch (qrc) {
14962306a36Sopenharmony_ci		case H_SUCCESS:
15062306a36Sopenharmony_ci			spin_lock_bh(&vscsi->intr_lock);
15162306a36Sopenharmony_ci			vscsi->flags &= ~PREP_FOR_SUSPEND_FLAGS;
15262306a36Sopenharmony_ci			spin_unlock_bh(&vscsi->intr_lock);
15362306a36Sopenharmony_ci			break;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		case H_HARDWARE:
15662306a36Sopenharmony_ci		case H_PARAMETER:
15762306a36Sopenharmony_ci			dev_err(&vscsi->dev, "unregister_command_q: error from h_free_crq %ld\n",
15862306a36Sopenharmony_ci				qrc);
15962306a36Sopenharmony_ci			rc = ERROR;
16062306a36Sopenharmony_ci			break;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		case H_BUSY:
16362306a36Sopenharmony_ci		case H_LONG_BUSY_ORDER_1_MSEC:
16462306a36Sopenharmony_ci			/* msleep not good for small values */
16562306a36Sopenharmony_ci			usleep_range(1000, 2000);
16662306a36Sopenharmony_ci			ticks += 1;
16762306a36Sopenharmony_ci			break;
16862306a36Sopenharmony_ci		case H_LONG_BUSY_ORDER_10_MSEC:
16962306a36Sopenharmony_ci			usleep_range(10000, 20000);
17062306a36Sopenharmony_ci			ticks += 10;
17162306a36Sopenharmony_ci			break;
17262306a36Sopenharmony_ci		case H_LONG_BUSY_ORDER_100_MSEC:
17362306a36Sopenharmony_ci			msleep(100);
17462306a36Sopenharmony_ci			ticks += 100;
17562306a36Sopenharmony_ci			break;
17662306a36Sopenharmony_ci		case H_LONG_BUSY_ORDER_1_SEC:
17762306a36Sopenharmony_ci			ssleep(1);
17862306a36Sopenharmony_ci			ticks += 1000;
17962306a36Sopenharmony_ci			break;
18062306a36Sopenharmony_ci		case H_LONG_BUSY_ORDER_10_SEC:
18162306a36Sopenharmony_ci			ssleep(10);
18262306a36Sopenharmony_ci			ticks += 10000;
18362306a36Sopenharmony_ci			break;
18462306a36Sopenharmony_ci		case H_LONG_BUSY_ORDER_100_SEC:
18562306a36Sopenharmony_ci			ssleep(100);
18662306a36Sopenharmony_ci			ticks += 100000;
18762306a36Sopenharmony_ci			break;
18862306a36Sopenharmony_ci		default:
18962306a36Sopenharmony_ci			dev_err(&vscsi->dev, "unregister_command_q: unknown error %ld from h_free_crq\n",
19062306a36Sopenharmony_ci				qrc);
19162306a36Sopenharmony_ci			rc = ERROR;
19262306a36Sopenharmony_ci			break;
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		/*
19662306a36Sopenharmony_ci		 * dont wait more then 300 seconds
19762306a36Sopenharmony_ci		 * ticks are in milliseconds more or less
19862306a36Sopenharmony_ci		 */
19962306a36Sopenharmony_ci		if (ticks > 300000 && qrc != H_SUCCESS) {
20062306a36Sopenharmony_ci			rc = ERROR;
20162306a36Sopenharmony_ci			dev_err(&vscsi->dev, "Excessive wait for h_free_crq\n");
20262306a36Sopenharmony_ci		}
20362306a36Sopenharmony_ci	} while (qrc != H_SUCCESS && rc == ADAPT_SUCCESS);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "Freeing CRQ: phyp rc %ld, rc %ld\n", qrc, rc);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return rc;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci/**
21162306a36Sopenharmony_ci * ibmvscsis_delete_client_info() - Helper function to Delete Client Info
21262306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
21362306a36Sopenharmony_ci * @client_closed:	True if client closed its queue
21462306a36Sopenharmony_ci *
21562306a36Sopenharmony_ci * Deletes information specific to the client when the client goes away
21662306a36Sopenharmony_ci *
21762306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
21862306a36Sopenharmony_ci *	Interrupt or Process
21962306a36Sopenharmony_ci */
22062306a36Sopenharmony_cistatic void ibmvscsis_delete_client_info(struct scsi_info *vscsi,
22162306a36Sopenharmony_ci					 bool client_closed)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	vscsi->client_cap = 0;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/*
22662306a36Sopenharmony_ci	 * Some things we don't want to clear if we're closing the queue,
22762306a36Sopenharmony_ci	 * because some clients don't resend the host handshake when they
22862306a36Sopenharmony_ci	 * get a transport event.
22962306a36Sopenharmony_ci	 */
23062306a36Sopenharmony_ci	if (client_closed)
23162306a36Sopenharmony_ci		vscsi->client_data.os_type = 0;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/**
23562306a36Sopenharmony_ci * ibmvscsis_free_command_q() - Free Command Queue
23662306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
23762306a36Sopenharmony_ci *
23862306a36Sopenharmony_ci * This function calls unregister_command_q, then clears interrupts and
23962306a36Sopenharmony_ci * any pending interrupt acknowledgments associated with the command q.
24062306a36Sopenharmony_ci * It also clears memory if there is no error.
24162306a36Sopenharmony_ci *
24262306a36Sopenharmony_ci * PHYP did not meet the PAPR architecture so that we must give up the
24362306a36Sopenharmony_ci * lock. This causes a timing hole regarding state change.  To close the
24462306a36Sopenharmony_ci * hole this routine does accounting on any change that occurred during
24562306a36Sopenharmony_ci * the time the lock is not held.
24662306a36Sopenharmony_ci * NOTE: must give up and then acquire the interrupt lock, the caller must
24762306a36Sopenharmony_ci *	 make sure that state and or flags will prevent interrupt handler from
24862306a36Sopenharmony_ci *	 scheduling work.
24962306a36Sopenharmony_ci *
25062306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
25162306a36Sopenharmony_ci *	Process level, interrupt lock is held
25262306a36Sopenharmony_ci */
25362306a36Sopenharmony_cistatic long ibmvscsis_free_command_q(struct scsi_info *vscsi)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	int bytes;
25662306a36Sopenharmony_ci	u32 flags_under_lock;
25762306a36Sopenharmony_ci	u16 state_under_lock;
25862306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (!(vscsi->flags & CRQ_CLOSED)) {
26162306a36Sopenharmony_ci		vio_disable_interrupts(vscsi->dma_dev);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		state_under_lock = vscsi->new_state;
26462306a36Sopenharmony_ci		flags_under_lock = vscsi->flags;
26562306a36Sopenharmony_ci		vscsi->phyp_acr_state = 0;
26662306a36Sopenharmony_ci		vscsi->phyp_acr_flags = 0;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		spin_unlock_bh(&vscsi->intr_lock);
26962306a36Sopenharmony_ci		rc = ibmvscsis_unregister_command_q(vscsi);
27062306a36Sopenharmony_ci		spin_lock_bh(&vscsi->intr_lock);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		if (state_under_lock != vscsi->new_state)
27362306a36Sopenharmony_ci			vscsi->phyp_acr_state = vscsi->new_state;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		vscsi->phyp_acr_flags = ((~flags_under_lock) & vscsi->flags);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		if (rc == ADAPT_SUCCESS) {
27862306a36Sopenharmony_ci			bytes = vscsi->cmd_q.size * PAGE_SIZE;
27962306a36Sopenharmony_ci			memset(vscsi->cmd_q.base_addr, 0, bytes);
28062306a36Sopenharmony_ci			vscsi->cmd_q.index = 0;
28162306a36Sopenharmony_ci			vscsi->flags |= CRQ_CLOSED;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci			ibmvscsis_delete_client_info(vscsi, false);
28462306a36Sopenharmony_ci		}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "free_command_q: flags 0x%x, state 0x%hx, acr_flags 0x%x, acr_state 0x%hx\n",
28762306a36Sopenharmony_ci			vscsi->flags, vscsi->state, vscsi->phyp_acr_flags,
28862306a36Sopenharmony_ci			vscsi->phyp_acr_state);
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci	return rc;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/**
29462306a36Sopenharmony_ci * ibmvscsis_cmd_q_dequeue() - Get valid Command element
29562306a36Sopenharmony_ci * @mask:	Mask to use in case index wraps
29662306a36Sopenharmony_ci * @current_index:	Current index into command queue
29762306a36Sopenharmony_ci * @base_addr:	Pointer to start of command queue
29862306a36Sopenharmony_ci *
29962306a36Sopenharmony_ci * Returns a pointer to a valid command element or NULL, if the command
30062306a36Sopenharmony_ci * queue is empty
30162306a36Sopenharmony_ci *
30262306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
30362306a36Sopenharmony_ci *	Interrupt environment, interrupt lock held
30462306a36Sopenharmony_ci */
30562306a36Sopenharmony_cistatic struct viosrp_crq *ibmvscsis_cmd_q_dequeue(uint mask,
30662306a36Sopenharmony_ci						  uint *current_index,
30762306a36Sopenharmony_ci						  struct viosrp_crq *base_addr)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct viosrp_crq *ptr;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	ptr = base_addr + *current_index;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (ptr->valid) {
31462306a36Sopenharmony_ci		*current_index = (*current_index + 1) & mask;
31562306a36Sopenharmony_ci		dma_rmb();
31662306a36Sopenharmony_ci	} else {
31762306a36Sopenharmony_ci		ptr = NULL;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return ptr;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci/**
32462306a36Sopenharmony_ci * ibmvscsis_send_init_message() - send initialize message to the client
32562306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
32662306a36Sopenharmony_ci * @format:	Which Init Message format to send
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
32962306a36Sopenharmony_ci *	Interrupt environment interrupt lock held
33062306a36Sopenharmony_ci */
33162306a36Sopenharmony_cistatic long ibmvscsis_send_init_message(struct scsi_info *vscsi, u8 format)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct viosrp_crq *crq;
33462306a36Sopenharmony_ci	u64 buffer[2] = { 0, 0 };
33562306a36Sopenharmony_ci	long rc;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	crq = (struct viosrp_crq *)&buffer;
33862306a36Sopenharmony_ci	crq->valid = VALID_INIT_MSG;
33962306a36Sopenharmony_ci	crq->format = format;
34062306a36Sopenharmony_ci	rc = h_send_crq(vscsi->dds.unit_id, cpu_to_be64(buffer[MSG_HI]),
34162306a36Sopenharmony_ci			cpu_to_be64(buffer[MSG_LOW]));
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return rc;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/**
34762306a36Sopenharmony_ci * ibmvscsis_check_init_msg() - Check init message valid
34862306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
34962306a36Sopenharmony_ci * @format:	Pointer to return format of Init Message, if any.
35062306a36Sopenharmony_ci *		Set to UNUSED_FORMAT if no Init Message in queue.
35162306a36Sopenharmony_ci *
35262306a36Sopenharmony_ci * Checks if an initialize message was queued by the initiatior
35362306a36Sopenharmony_ci * after the queue was created and before the interrupt was enabled.
35462306a36Sopenharmony_ci *
35562306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
35662306a36Sopenharmony_ci *	Process level only, interrupt lock held
35762306a36Sopenharmony_ci */
35862306a36Sopenharmony_cistatic long ibmvscsis_check_init_msg(struct scsi_info *vscsi, uint *format)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct viosrp_crq *crq;
36162306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	crq = ibmvscsis_cmd_q_dequeue(vscsi->cmd_q.mask, &vscsi->cmd_q.index,
36462306a36Sopenharmony_ci				      vscsi->cmd_q.base_addr);
36562306a36Sopenharmony_ci	if (!crq) {
36662306a36Sopenharmony_ci		*format = (uint)UNUSED_FORMAT;
36762306a36Sopenharmony_ci	} else if (crq->valid == VALID_INIT_MSG && crq->format == INIT_MSG) {
36862306a36Sopenharmony_ci		*format = (uint)INIT_MSG;
36962306a36Sopenharmony_ci		crq->valid = INVALIDATE_CMD_RESP_EL;
37062306a36Sopenharmony_ci		dma_rmb();
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		/*
37362306a36Sopenharmony_ci		 * the caller has ensured no initialize message was
37462306a36Sopenharmony_ci		 * sent after the queue was
37562306a36Sopenharmony_ci		 * created so there should be no other message on the queue.
37662306a36Sopenharmony_ci		 */
37762306a36Sopenharmony_ci		crq = ibmvscsis_cmd_q_dequeue(vscsi->cmd_q.mask,
37862306a36Sopenharmony_ci					      &vscsi->cmd_q.index,
37962306a36Sopenharmony_ci					      vscsi->cmd_q.base_addr);
38062306a36Sopenharmony_ci		if (crq) {
38162306a36Sopenharmony_ci			*format = (uint)(crq->format);
38262306a36Sopenharmony_ci			rc = ERROR;
38362306a36Sopenharmony_ci			crq->valid = INVALIDATE_CMD_RESP_EL;
38462306a36Sopenharmony_ci			dma_rmb();
38562306a36Sopenharmony_ci		}
38662306a36Sopenharmony_ci	} else {
38762306a36Sopenharmony_ci		*format = (uint)(crq->format);
38862306a36Sopenharmony_ci		rc = ERROR;
38962306a36Sopenharmony_ci		crq->valid = INVALIDATE_CMD_RESP_EL;
39062306a36Sopenharmony_ci		dma_rmb();
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return rc;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/**
39762306a36Sopenharmony_ci * ibmvscsis_disconnect() - Helper function to disconnect
39862306a36Sopenharmony_ci * @work:	Pointer to work_struct, gives access to our adapter structure
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * An error has occurred or the driver received a Transport event,
40162306a36Sopenharmony_ci * and the driver is requesting that the command queue be de-registered
40262306a36Sopenharmony_ci * in a safe manner. If there is no outstanding I/O then we can stop the
40362306a36Sopenharmony_ci * queue. If we are restarting the queue it will be reflected in the
40462306a36Sopenharmony_ci * the state of the adapter.
40562306a36Sopenharmony_ci *
40662306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
40762306a36Sopenharmony_ci *	Process environment
40862306a36Sopenharmony_ci */
40962306a36Sopenharmony_cistatic void ibmvscsis_disconnect(struct work_struct *work)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct scsi_info *vscsi = container_of(work, struct scsi_info,
41262306a36Sopenharmony_ci					       proc_work);
41362306a36Sopenharmony_ci	u16 new_state;
41462306a36Sopenharmony_ci	bool wait_idle = false;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	spin_lock_bh(&vscsi->intr_lock);
41762306a36Sopenharmony_ci	new_state = vscsi->new_state;
41862306a36Sopenharmony_ci	vscsi->new_state = 0;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	vscsi->flags |= DISCONNECT_SCHEDULED;
42162306a36Sopenharmony_ci	vscsi->flags &= ~SCHEDULE_DISCONNECT;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "disconnect: flags 0x%x, state 0x%hx\n",
42462306a36Sopenharmony_ci		vscsi->flags, vscsi->state);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/*
42762306a36Sopenharmony_ci	 * check which state we are in and see if we
42862306a36Sopenharmony_ci	 * should transitition to the new state
42962306a36Sopenharmony_ci	 */
43062306a36Sopenharmony_ci	switch (vscsi->state) {
43162306a36Sopenharmony_ci	/* Should never be called while in this state. */
43262306a36Sopenharmony_ci	case NO_QUEUE:
43362306a36Sopenharmony_ci	/*
43462306a36Sopenharmony_ci	 * Can never transition from this state;
43562306a36Sopenharmony_ci	 * igonore errors and logout.
43662306a36Sopenharmony_ci	 */
43762306a36Sopenharmony_ci	case UNCONFIGURING:
43862306a36Sopenharmony_ci		break;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/* can transition from this state to UNCONFIGURING */
44162306a36Sopenharmony_ci	case ERR_DISCONNECT:
44262306a36Sopenharmony_ci		if (new_state == UNCONFIGURING)
44362306a36Sopenharmony_ci			vscsi->state = new_state;
44462306a36Sopenharmony_ci		break;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/*
44762306a36Sopenharmony_ci	 * Can transition from this state to unconfiguring
44862306a36Sopenharmony_ci	 * or err disconnect.
44962306a36Sopenharmony_ci	 */
45062306a36Sopenharmony_ci	case ERR_DISCONNECT_RECONNECT:
45162306a36Sopenharmony_ci		switch (new_state) {
45262306a36Sopenharmony_ci		case UNCONFIGURING:
45362306a36Sopenharmony_ci		case ERR_DISCONNECT:
45462306a36Sopenharmony_ci			vscsi->state = new_state;
45562306a36Sopenharmony_ci			break;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		case WAIT_IDLE:
45862306a36Sopenharmony_ci			break;
45962306a36Sopenharmony_ci		default:
46062306a36Sopenharmony_ci			break;
46162306a36Sopenharmony_ci		}
46262306a36Sopenharmony_ci		break;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/* can transition from this state to UNCONFIGURING */
46562306a36Sopenharmony_ci	case ERR_DISCONNECTED:
46662306a36Sopenharmony_ci		if (new_state == UNCONFIGURING)
46762306a36Sopenharmony_ci			vscsi->state = new_state;
46862306a36Sopenharmony_ci		break;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	case WAIT_ENABLED:
47162306a36Sopenharmony_ci		switch (new_state) {
47262306a36Sopenharmony_ci		case UNCONFIGURING:
47362306a36Sopenharmony_ci			vscsi->state = new_state;
47462306a36Sopenharmony_ci			vscsi->flags |= RESPONSE_Q_DOWN;
47562306a36Sopenharmony_ci			vscsi->flags &= ~(SCHEDULE_DISCONNECT |
47662306a36Sopenharmony_ci					  DISCONNECT_SCHEDULED);
47762306a36Sopenharmony_ci			dma_rmb();
47862306a36Sopenharmony_ci			if (vscsi->flags & CFG_SLEEPING) {
47962306a36Sopenharmony_ci				vscsi->flags &= ~CFG_SLEEPING;
48062306a36Sopenharmony_ci				complete(&vscsi->unconfig);
48162306a36Sopenharmony_ci			}
48262306a36Sopenharmony_ci			break;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		/* should never happen */
48562306a36Sopenharmony_ci		case ERR_DISCONNECT:
48662306a36Sopenharmony_ci		case ERR_DISCONNECT_RECONNECT:
48762306a36Sopenharmony_ci		case WAIT_IDLE:
48862306a36Sopenharmony_ci			dev_err(&vscsi->dev, "disconnect: invalid state %d for WAIT_IDLE\n",
48962306a36Sopenharmony_ci				vscsi->state);
49062306a36Sopenharmony_ci			break;
49162306a36Sopenharmony_ci		}
49262306a36Sopenharmony_ci		break;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	case WAIT_IDLE:
49562306a36Sopenharmony_ci		switch (new_state) {
49662306a36Sopenharmony_ci		case UNCONFIGURING:
49762306a36Sopenharmony_ci			vscsi->flags |= RESPONSE_Q_DOWN;
49862306a36Sopenharmony_ci			vscsi->state = new_state;
49962306a36Sopenharmony_ci			vscsi->flags &= ~(SCHEDULE_DISCONNECT |
50062306a36Sopenharmony_ci					  DISCONNECT_SCHEDULED);
50162306a36Sopenharmony_ci			ibmvscsis_free_command_q(vscsi);
50262306a36Sopenharmony_ci			break;
50362306a36Sopenharmony_ci		case ERR_DISCONNECT:
50462306a36Sopenharmony_ci		case ERR_DISCONNECT_RECONNECT:
50562306a36Sopenharmony_ci			vscsi->state = new_state;
50662306a36Sopenharmony_ci			break;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci		break;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/*
51162306a36Sopenharmony_ci	 * Initiator has not done a successful srp login
51262306a36Sopenharmony_ci	 * or has done a successful srp logout ( adapter was not
51362306a36Sopenharmony_ci	 * busy). In the first case there can be responses queued
51462306a36Sopenharmony_ci	 * waiting for space on the initiators response queue (MAD)
51562306a36Sopenharmony_ci	 * The second case the adapter is idle. Assume the worse case,
51662306a36Sopenharmony_ci	 * i.e. the second case.
51762306a36Sopenharmony_ci	 */
51862306a36Sopenharmony_ci	case WAIT_CONNECTION:
51962306a36Sopenharmony_ci	case CONNECTED:
52062306a36Sopenharmony_ci	case SRP_PROCESSING:
52162306a36Sopenharmony_ci		wait_idle = true;
52262306a36Sopenharmony_ci		vscsi->state = new_state;
52362306a36Sopenharmony_ci		break;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	/* can transition from this state to UNCONFIGURING */
52662306a36Sopenharmony_ci	case UNDEFINED:
52762306a36Sopenharmony_ci		if (new_state == UNCONFIGURING)
52862306a36Sopenharmony_ci			vscsi->state = new_state;
52962306a36Sopenharmony_ci		break;
53062306a36Sopenharmony_ci	default:
53162306a36Sopenharmony_ci		break;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (wait_idle) {
53562306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "disconnect start wait, active %d, sched %d\n",
53662306a36Sopenharmony_ci			(int)list_empty(&vscsi->active_q),
53762306a36Sopenharmony_ci			(int)list_empty(&vscsi->schedule_q));
53862306a36Sopenharmony_ci		if (!list_empty(&vscsi->active_q) ||
53962306a36Sopenharmony_ci		    !list_empty(&vscsi->schedule_q)) {
54062306a36Sopenharmony_ci			vscsi->flags |= WAIT_FOR_IDLE;
54162306a36Sopenharmony_ci			dev_dbg(&vscsi->dev, "disconnect flags 0x%x\n",
54262306a36Sopenharmony_ci				vscsi->flags);
54362306a36Sopenharmony_ci			/*
54462306a36Sopenharmony_ci			 * This routine is can not be called with the interrupt
54562306a36Sopenharmony_ci			 * lock held.
54662306a36Sopenharmony_ci			 */
54762306a36Sopenharmony_ci			spin_unlock_bh(&vscsi->intr_lock);
54862306a36Sopenharmony_ci			wait_for_completion(&vscsi->wait_idle);
54962306a36Sopenharmony_ci			spin_lock_bh(&vscsi->intr_lock);
55062306a36Sopenharmony_ci		}
55162306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "disconnect stop wait\n");
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		ibmvscsis_adapter_idle(vscsi);
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	spin_unlock_bh(&vscsi->intr_lock);
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci/**
56062306a36Sopenharmony_ci * ibmvscsis_post_disconnect() - Schedule the disconnect
56162306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
56262306a36Sopenharmony_ci * @new_state:	State to move to after disconnecting
56362306a36Sopenharmony_ci * @flag_bits:	Flags to turn on in adapter structure
56462306a36Sopenharmony_ci *
56562306a36Sopenharmony_ci * If it's already been scheduled, then see if we need to "upgrade"
56662306a36Sopenharmony_ci * the new state (if the one passed in is more "severe" than the
56762306a36Sopenharmony_ci * previous one).
56862306a36Sopenharmony_ci *
56962306a36Sopenharmony_ci * PRECONDITION:
57062306a36Sopenharmony_ci *	interrupt lock is held
57162306a36Sopenharmony_ci */
57262306a36Sopenharmony_cistatic void ibmvscsis_post_disconnect(struct scsi_info *vscsi, uint new_state,
57362306a36Sopenharmony_ci				      uint flag_bits)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	uint state;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	/* check the validity of the new state */
57862306a36Sopenharmony_ci	switch (new_state) {
57962306a36Sopenharmony_ci	case UNCONFIGURING:
58062306a36Sopenharmony_ci	case ERR_DISCONNECT:
58162306a36Sopenharmony_ci	case ERR_DISCONNECT_RECONNECT:
58262306a36Sopenharmony_ci	case WAIT_IDLE:
58362306a36Sopenharmony_ci		break;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	default:
58662306a36Sopenharmony_ci		dev_err(&vscsi->dev, "post_disconnect: Invalid new state %d\n",
58762306a36Sopenharmony_ci			new_state);
58862306a36Sopenharmony_ci		return;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	vscsi->flags |= flag_bits;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "post_disconnect: new_state 0x%x, flag_bits 0x%x, vscsi->flags 0x%x, state %hx\n",
59462306a36Sopenharmony_ci		new_state, flag_bits, vscsi->flags, vscsi->state);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	if (!(vscsi->flags & (DISCONNECT_SCHEDULED | SCHEDULE_DISCONNECT))) {
59762306a36Sopenharmony_ci		vscsi->flags |= SCHEDULE_DISCONNECT;
59862306a36Sopenharmony_ci		vscsi->new_state = new_state;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		INIT_WORK(&vscsi->proc_work, ibmvscsis_disconnect);
60162306a36Sopenharmony_ci		(void)queue_work(vscsi->work_q, &vscsi->proc_work);
60262306a36Sopenharmony_ci	} else {
60362306a36Sopenharmony_ci		if (vscsi->new_state)
60462306a36Sopenharmony_ci			state = vscsi->new_state;
60562306a36Sopenharmony_ci		else
60662306a36Sopenharmony_ci			state = vscsi->state;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		switch (state) {
60962306a36Sopenharmony_ci		case NO_QUEUE:
61062306a36Sopenharmony_ci		case UNCONFIGURING:
61162306a36Sopenharmony_ci			break;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci		case ERR_DISCONNECTED:
61462306a36Sopenharmony_ci		case ERR_DISCONNECT:
61562306a36Sopenharmony_ci		case UNDEFINED:
61662306a36Sopenharmony_ci			if (new_state == UNCONFIGURING)
61762306a36Sopenharmony_ci				vscsi->new_state = new_state;
61862306a36Sopenharmony_ci			break;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		case ERR_DISCONNECT_RECONNECT:
62162306a36Sopenharmony_ci			switch (new_state) {
62262306a36Sopenharmony_ci			case UNCONFIGURING:
62362306a36Sopenharmony_ci			case ERR_DISCONNECT:
62462306a36Sopenharmony_ci				vscsi->new_state = new_state;
62562306a36Sopenharmony_ci				break;
62662306a36Sopenharmony_ci			default:
62762306a36Sopenharmony_ci				break;
62862306a36Sopenharmony_ci			}
62962306a36Sopenharmony_ci			break;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		case WAIT_ENABLED:
63262306a36Sopenharmony_ci		case WAIT_IDLE:
63362306a36Sopenharmony_ci		case WAIT_CONNECTION:
63462306a36Sopenharmony_ci		case CONNECTED:
63562306a36Sopenharmony_ci		case SRP_PROCESSING:
63662306a36Sopenharmony_ci			vscsi->new_state = new_state;
63762306a36Sopenharmony_ci			break;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci		default:
64062306a36Sopenharmony_ci			break;
64162306a36Sopenharmony_ci		}
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "Leaving post_disconnect: flags 0x%x, new_state 0x%x\n",
64562306a36Sopenharmony_ci		vscsi->flags, vscsi->new_state);
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci/**
64962306a36Sopenharmony_ci * ibmvscsis_handle_init_compl_msg() - Respond to an Init Complete Message
65062306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
65162306a36Sopenharmony_ci *
65262306a36Sopenharmony_ci * Must be called with interrupt lock held.
65362306a36Sopenharmony_ci */
65462306a36Sopenharmony_cistatic long ibmvscsis_handle_init_compl_msg(struct scsi_info *vscsi)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	switch (vscsi->state) {
65962306a36Sopenharmony_ci	case NO_QUEUE:
66062306a36Sopenharmony_ci	case ERR_DISCONNECT:
66162306a36Sopenharmony_ci	case ERR_DISCONNECT_RECONNECT:
66262306a36Sopenharmony_ci	case ERR_DISCONNECTED:
66362306a36Sopenharmony_ci	case UNCONFIGURING:
66462306a36Sopenharmony_ci	case UNDEFINED:
66562306a36Sopenharmony_ci		rc = ERROR;
66662306a36Sopenharmony_ci		break;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	case WAIT_CONNECTION:
66962306a36Sopenharmony_ci		vscsi->state = CONNECTED;
67062306a36Sopenharmony_ci		break;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	case WAIT_IDLE:
67362306a36Sopenharmony_ci	case SRP_PROCESSING:
67462306a36Sopenharmony_ci	case CONNECTED:
67562306a36Sopenharmony_ci	case WAIT_ENABLED:
67662306a36Sopenharmony_ci	default:
67762306a36Sopenharmony_ci		rc = ERROR;
67862306a36Sopenharmony_ci		dev_err(&vscsi->dev, "init_msg: invalid state %d to get init compl msg\n",
67962306a36Sopenharmony_ci			vscsi->state);
68062306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
68162306a36Sopenharmony_ci		break;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	return rc;
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci/**
68862306a36Sopenharmony_ci * ibmvscsis_handle_init_msg() - Respond to an Init Message
68962306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
69062306a36Sopenharmony_ci *
69162306a36Sopenharmony_ci * Must be called with interrupt lock held.
69262306a36Sopenharmony_ci */
69362306a36Sopenharmony_cistatic long ibmvscsis_handle_init_msg(struct scsi_info *vscsi)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	switch (vscsi->state) {
69862306a36Sopenharmony_ci	case WAIT_CONNECTION:
69962306a36Sopenharmony_ci		rc = ibmvscsis_send_init_message(vscsi, INIT_COMPLETE_MSG);
70062306a36Sopenharmony_ci		switch (rc) {
70162306a36Sopenharmony_ci		case H_SUCCESS:
70262306a36Sopenharmony_ci			vscsi->state = CONNECTED;
70362306a36Sopenharmony_ci			break;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		case H_PARAMETER:
70662306a36Sopenharmony_ci			dev_err(&vscsi->dev, "init_msg: failed to send, rc %ld\n",
70762306a36Sopenharmony_ci				rc);
70862306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0);
70962306a36Sopenharmony_ci			break;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci		case H_DROPPED:
71262306a36Sopenharmony_ci			dev_err(&vscsi->dev, "init_msg: failed to send, rc %ld\n",
71362306a36Sopenharmony_ci				rc);
71462306a36Sopenharmony_ci			rc = ERROR;
71562306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi,
71662306a36Sopenharmony_ci						  ERR_DISCONNECT_RECONNECT, 0);
71762306a36Sopenharmony_ci			break;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci		case H_CLOSED:
72062306a36Sopenharmony_ci			dev_warn(&vscsi->dev, "init_msg: failed to send, rc %ld\n",
72162306a36Sopenharmony_ci				 rc);
72262306a36Sopenharmony_ci			rc = 0;
72362306a36Sopenharmony_ci			break;
72462306a36Sopenharmony_ci		}
72562306a36Sopenharmony_ci		break;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	case UNDEFINED:
72862306a36Sopenharmony_ci		rc = ERROR;
72962306a36Sopenharmony_ci		break;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	case UNCONFIGURING:
73262306a36Sopenharmony_ci		break;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	case WAIT_ENABLED:
73562306a36Sopenharmony_ci	case CONNECTED:
73662306a36Sopenharmony_ci	case SRP_PROCESSING:
73762306a36Sopenharmony_ci	case WAIT_IDLE:
73862306a36Sopenharmony_ci	case NO_QUEUE:
73962306a36Sopenharmony_ci	case ERR_DISCONNECT:
74062306a36Sopenharmony_ci	case ERR_DISCONNECT_RECONNECT:
74162306a36Sopenharmony_ci	case ERR_DISCONNECTED:
74262306a36Sopenharmony_ci	default:
74362306a36Sopenharmony_ci		rc = ERROR;
74462306a36Sopenharmony_ci		dev_err(&vscsi->dev, "init_msg: invalid state %d to get init msg\n",
74562306a36Sopenharmony_ci			vscsi->state);
74662306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
74762306a36Sopenharmony_ci		break;
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	return rc;
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci/**
75462306a36Sopenharmony_ci * ibmvscsis_init_msg() - Respond to an init message
75562306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
75662306a36Sopenharmony_ci * @crq:	Pointer to CRQ element containing the Init Message
75762306a36Sopenharmony_ci *
75862306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
75962306a36Sopenharmony_ci *	Interrupt, interrupt lock held
76062306a36Sopenharmony_ci */
76162306a36Sopenharmony_cistatic long ibmvscsis_init_msg(struct scsi_info *vscsi, struct viosrp_crq *crq)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "init_msg: state 0x%hx\n", vscsi->state);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	rc = h_vioctl(vscsi->dds.unit_id, H_GET_PARTNER_INFO,
76862306a36Sopenharmony_ci		      (u64)vscsi->map_ioba | ((u64)PAGE_SIZE << 32), 0, 0, 0,
76962306a36Sopenharmony_ci		      0);
77062306a36Sopenharmony_ci	if (rc == H_SUCCESS) {
77162306a36Sopenharmony_ci		vscsi->client_data.partition_number =
77262306a36Sopenharmony_ci			be64_to_cpu(*(u64 *)vscsi->map_buf);
77362306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "init_msg, part num %d\n",
77462306a36Sopenharmony_ci			vscsi->client_data.partition_number);
77562306a36Sopenharmony_ci	} else {
77662306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "init_msg h_vioctl rc %ld\n", rc);
77762306a36Sopenharmony_ci		rc = ADAPT_SUCCESS;
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	if (crq->format == INIT_MSG) {
78162306a36Sopenharmony_ci		rc = ibmvscsis_handle_init_msg(vscsi);
78262306a36Sopenharmony_ci	} else if (crq->format == INIT_COMPLETE_MSG) {
78362306a36Sopenharmony_ci		rc = ibmvscsis_handle_init_compl_msg(vscsi);
78462306a36Sopenharmony_ci	} else {
78562306a36Sopenharmony_ci		rc = ERROR;
78662306a36Sopenharmony_ci		dev_err(&vscsi->dev, "init_msg: invalid format %d\n",
78762306a36Sopenharmony_ci			(uint)crq->format);
78862306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	return rc;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci/**
79562306a36Sopenharmony_ci * ibmvscsis_establish_new_q() - Establish new CRQ queue
79662306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
79762306a36Sopenharmony_ci *
79862306a36Sopenharmony_ci * Must be called with interrupt lock held.
79962306a36Sopenharmony_ci */
80062306a36Sopenharmony_cistatic long ibmvscsis_establish_new_q(struct scsi_info *vscsi)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
80362306a36Sopenharmony_ci	uint format;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	rc = h_vioctl(vscsi->dds.unit_id, H_ENABLE_PREPARE_FOR_SUSPEND, 30000,
80662306a36Sopenharmony_ci		      0, 0, 0, 0);
80762306a36Sopenharmony_ci	if (rc == H_SUCCESS)
80862306a36Sopenharmony_ci		vscsi->flags |= PREP_FOR_SUSPEND_ENABLED;
80962306a36Sopenharmony_ci	else if (rc != H_NOT_FOUND)
81062306a36Sopenharmony_ci		dev_err(&vscsi->dev, "Error from Enable Prepare for Suspend: %ld\n",
81162306a36Sopenharmony_ci			rc);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	vscsi->flags &= PRESERVE_FLAG_FIELDS;
81462306a36Sopenharmony_ci	vscsi->rsp_q_timer.timer_pops = 0;
81562306a36Sopenharmony_ci	vscsi->debit = 0;
81662306a36Sopenharmony_ci	vscsi->credit = 0;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	rc = vio_enable_interrupts(vscsi->dma_dev);
81962306a36Sopenharmony_ci	if (rc) {
82062306a36Sopenharmony_ci		dev_warn(&vscsi->dev, "establish_new_q: failed to enable interrupts, rc %ld\n",
82162306a36Sopenharmony_ci			 rc);
82262306a36Sopenharmony_ci		return rc;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	rc = ibmvscsis_check_init_msg(vscsi, &format);
82662306a36Sopenharmony_ci	if (rc) {
82762306a36Sopenharmony_ci		dev_err(&vscsi->dev, "establish_new_q: check_init_msg failed, rc %ld\n",
82862306a36Sopenharmony_ci			rc);
82962306a36Sopenharmony_ci		return rc;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (format == UNUSED_FORMAT) {
83362306a36Sopenharmony_ci		rc = ibmvscsis_send_init_message(vscsi, INIT_MSG);
83462306a36Sopenharmony_ci		switch (rc) {
83562306a36Sopenharmony_ci		case H_SUCCESS:
83662306a36Sopenharmony_ci		case H_DROPPED:
83762306a36Sopenharmony_ci		case H_CLOSED:
83862306a36Sopenharmony_ci			rc = ADAPT_SUCCESS;
83962306a36Sopenharmony_ci			break;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci		case H_PARAMETER:
84262306a36Sopenharmony_ci		case H_HARDWARE:
84362306a36Sopenharmony_ci			break;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci		default:
84662306a36Sopenharmony_ci			vscsi->state = UNDEFINED;
84762306a36Sopenharmony_ci			rc = H_HARDWARE;
84862306a36Sopenharmony_ci			break;
84962306a36Sopenharmony_ci		}
85062306a36Sopenharmony_ci	} else if (format == INIT_MSG) {
85162306a36Sopenharmony_ci		rc = ibmvscsis_handle_init_msg(vscsi);
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	return rc;
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci/**
85862306a36Sopenharmony_ci * ibmvscsis_reset_queue() - Reset CRQ Queue
85962306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
86062306a36Sopenharmony_ci *
86162306a36Sopenharmony_ci * This function calls h_free_q and then calls h_reg_q and does all
86262306a36Sopenharmony_ci * of the bookkeeping to get us back to where we can communicate.
86362306a36Sopenharmony_ci *
86462306a36Sopenharmony_ci * Actually, we don't always call h_free_crq.  A problem was discovered
86562306a36Sopenharmony_ci * where one partition would close and reopen his queue, which would
86662306a36Sopenharmony_ci * cause his partner to get a transport event, which would cause him to
86762306a36Sopenharmony_ci * close and reopen his queue, which would cause the original partition
86862306a36Sopenharmony_ci * to get a transport event, etc., etc.  To prevent this, we don't
86962306a36Sopenharmony_ci * actually close our queue if the client initiated the reset, (i.e.
87062306a36Sopenharmony_ci * either we got a transport event or we have detected that the client's
87162306a36Sopenharmony_ci * queue is gone)
87262306a36Sopenharmony_ci *
87362306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
87462306a36Sopenharmony_ci *	Process environment, called with interrupt lock held
87562306a36Sopenharmony_ci */
87662306a36Sopenharmony_cistatic void ibmvscsis_reset_queue(struct scsi_info *vscsi)
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	int bytes;
87962306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "reset_queue: flags 0x%x\n", vscsi->flags);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	/* don't reset, the client did it for us */
88462306a36Sopenharmony_ci	if (vscsi->flags & (CLIENT_FAILED | TRANS_EVENT)) {
88562306a36Sopenharmony_ci		vscsi->flags &= PRESERVE_FLAG_FIELDS;
88662306a36Sopenharmony_ci		vscsi->rsp_q_timer.timer_pops = 0;
88762306a36Sopenharmony_ci		vscsi->debit = 0;
88862306a36Sopenharmony_ci		vscsi->credit = 0;
88962306a36Sopenharmony_ci		vscsi->state = WAIT_CONNECTION;
89062306a36Sopenharmony_ci		vio_enable_interrupts(vscsi->dma_dev);
89162306a36Sopenharmony_ci	} else {
89262306a36Sopenharmony_ci		rc = ibmvscsis_free_command_q(vscsi);
89362306a36Sopenharmony_ci		if (rc == ADAPT_SUCCESS) {
89462306a36Sopenharmony_ci			vscsi->state = WAIT_CONNECTION;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci			bytes = vscsi->cmd_q.size * PAGE_SIZE;
89762306a36Sopenharmony_ci			rc = h_reg_crq(vscsi->dds.unit_id,
89862306a36Sopenharmony_ci				       vscsi->cmd_q.crq_token, bytes);
89962306a36Sopenharmony_ci			if (rc == H_CLOSED || rc == H_SUCCESS) {
90062306a36Sopenharmony_ci				rc = ibmvscsis_establish_new_q(vscsi);
90162306a36Sopenharmony_ci			}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci			if (rc != ADAPT_SUCCESS) {
90462306a36Sopenharmony_ci				dev_dbg(&vscsi->dev, "reset_queue: reg_crq rc %ld\n",
90562306a36Sopenharmony_ci					rc);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci				vscsi->state = ERR_DISCONNECTED;
90862306a36Sopenharmony_ci				vscsi->flags |= RESPONSE_Q_DOWN;
90962306a36Sopenharmony_ci				ibmvscsis_free_command_q(vscsi);
91062306a36Sopenharmony_ci			}
91162306a36Sopenharmony_ci		} else {
91262306a36Sopenharmony_ci			vscsi->state = ERR_DISCONNECTED;
91362306a36Sopenharmony_ci			vscsi->flags |= RESPONSE_Q_DOWN;
91462306a36Sopenharmony_ci		}
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/**
91962306a36Sopenharmony_ci * ibmvscsis_free_cmd_resources() - Free command resources
92062306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
92162306a36Sopenharmony_ci * @cmd:	Command which is not longer in use
92262306a36Sopenharmony_ci *
92362306a36Sopenharmony_ci * Must be called with interrupt lock held.
92462306a36Sopenharmony_ci */
92562306a36Sopenharmony_cistatic void ibmvscsis_free_cmd_resources(struct scsi_info *vscsi,
92662306a36Sopenharmony_ci					 struct ibmvscsis_cmd *cmd)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	switch (cmd->type) {
93162306a36Sopenharmony_ci	case TASK_MANAGEMENT:
93262306a36Sopenharmony_ci	case SCSI_CDB:
93362306a36Sopenharmony_ci		/*
93462306a36Sopenharmony_ci		 * When the queue goes down this value is cleared, so it
93562306a36Sopenharmony_ci		 * cannot be cleared in this general purpose function.
93662306a36Sopenharmony_ci		 */
93762306a36Sopenharmony_ci		if (vscsi->debit)
93862306a36Sopenharmony_ci			vscsi->debit -= 1;
93962306a36Sopenharmony_ci		break;
94062306a36Sopenharmony_ci	case ADAPTER_MAD:
94162306a36Sopenharmony_ci		vscsi->flags &= ~PROCESSING_MAD;
94262306a36Sopenharmony_ci		break;
94362306a36Sopenharmony_ci	case UNSET_TYPE:
94462306a36Sopenharmony_ci		break;
94562306a36Sopenharmony_ci	default:
94662306a36Sopenharmony_ci		dev_err(&vscsi->dev, "free_cmd_resources unknown type %d\n",
94762306a36Sopenharmony_ci			cmd->type);
94862306a36Sopenharmony_ci		break;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	cmd->iue = NULL;
95262306a36Sopenharmony_ci	list_add_tail(&cmd->list, &vscsi->free_cmd);
95362306a36Sopenharmony_ci	srp_iu_put(iue);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	if (list_empty(&vscsi->active_q) && list_empty(&vscsi->schedule_q) &&
95662306a36Sopenharmony_ci	    list_empty(&vscsi->waiting_rsp) && (vscsi->flags & WAIT_FOR_IDLE)) {
95762306a36Sopenharmony_ci		vscsi->flags &= ~WAIT_FOR_IDLE;
95862306a36Sopenharmony_ci		complete(&vscsi->wait_idle);
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci/**
96362306a36Sopenharmony_ci * ibmvscsis_ready_for_suspend() - Helper function to call VIOCTL
96462306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
96562306a36Sopenharmony_ci * @idle:	Indicates whether we were called from adapter_idle.  This
96662306a36Sopenharmony_ci *		is important to know if we need to do a disconnect, since if
96762306a36Sopenharmony_ci *		we're called from adapter_idle, we're still processing the
96862306a36Sopenharmony_ci *		current disconnect, so we can't just call post_disconnect.
96962306a36Sopenharmony_ci *
97062306a36Sopenharmony_ci * This function is called when the adapter is idle when phyp has sent
97162306a36Sopenharmony_ci * us a Prepare for Suspend Transport Event.
97262306a36Sopenharmony_ci *
97362306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
97462306a36Sopenharmony_ci *	Process or interrupt environment called with interrupt lock held
97562306a36Sopenharmony_ci */
97662306a36Sopenharmony_cistatic long ibmvscsis_ready_for_suspend(struct scsi_info *vscsi, bool idle)
97762306a36Sopenharmony_ci{
97862306a36Sopenharmony_ci	long rc = 0;
97962306a36Sopenharmony_ci	struct viosrp_crq *crq;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	/* See if there is a Resume event in the queue */
98262306a36Sopenharmony_ci	crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "ready_suspend: flags 0x%x, state 0x%hx crq_valid:%x\n",
98562306a36Sopenharmony_ci		vscsi->flags, vscsi->state, (int)crq->valid);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	if (!(vscsi->flags & PREP_FOR_SUSPEND_ABORTED) && !(crq->valid)) {
98862306a36Sopenharmony_ci		rc = h_vioctl(vscsi->dds.unit_id, H_READY_FOR_SUSPEND, 0, 0, 0,
98962306a36Sopenharmony_ci			      0, 0);
99062306a36Sopenharmony_ci		if (rc) {
99162306a36Sopenharmony_ci			dev_err(&vscsi->dev, "Ready for Suspend Vioctl failed: %ld\n",
99262306a36Sopenharmony_ci				rc);
99362306a36Sopenharmony_ci			rc = 0;
99462306a36Sopenharmony_ci		}
99562306a36Sopenharmony_ci	} else if (((vscsi->flags & PREP_FOR_SUSPEND_OVERWRITE) &&
99662306a36Sopenharmony_ci		    (vscsi->flags & PREP_FOR_SUSPEND_ABORTED)) ||
99762306a36Sopenharmony_ci		   ((crq->valid) && ((crq->valid != VALID_TRANS_EVENT) ||
99862306a36Sopenharmony_ci				     (crq->format != RESUME_FROM_SUSP)))) {
99962306a36Sopenharmony_ci		if (idle) {
100062306a36Sopenharmony_ci			vscsi->state = ERR_DISCONNECT_RECONNECT;
100162306a36Sopenharmony_ci			ibmvscsis_reset_queue(vscsi);
100262306a36Sopenharmony_ci			rc = -1;
100362306a36Sopenharmony_ci		} else if (vscsi->state == CONNECTED) {
100462306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi,
100562306a36Sopenharmony_ci						  ERR_DISCONNECT_RECONNECT, 0);
100662306a36Sopenharmony_ci		}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci		vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci		if ((crq->valid) && ((crq->valid != VALID_TRANS_EVENT) ||
101162306a36Sopenharmony_ci				     (crq->format != RESUME_FROM_SUSP)))
101262306a36Sopenharmony_ci			dev_err(&vscsi->dev, "Invalid element in CRQ after Prepare for Suspend");
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	vscsi->flags &= ~(PREP_FOR_SUSPEND_PENDING | PREP_FOR_SUSPEND_ABORTED);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	return rc;
101862306a36Sopenharmony_ci}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci/**
102162306a36Sopenharmony_ci * ibmvscsis_trans_event() - Handle a Transport Event
102262306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
102362306a36Sopenharmony_ci * @crq:	Pointer to CRQ entry containing the Transport Event
102462306a36Sopenharmony_ci *
102562306a36Sopenharmony_ci * Do the logic to close the I_T nexus.  This function may not
102662306a36Sopenharmony_ci * behave to specification.
102762306a36Sopenharmony_ci *
102862306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
102962306a36Sopenharmony_ci *	Interrupt, interrupt lock held
103062306a36Sopenharmony_ci */
103162306a36Sopenharmony_cistatic long ibmvscsis_trans_event(struct scsi_info *vscsi,
103262306a36Sopenharmony_ci				  struct viosrp_crq *crq)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "trans_event: format %d, flags 0x%x, state 0x%hx\n",
103762306a36Sopenharmony_ci		(int)crq->format, vscsi->flags, vscsi->state);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	switch (crq->format) {
104062306a36Sopenharmony_ci	case MIGRATED:
104162306a36Sopenharmony_ci	case PARTNER_FAILED:
104262306a36Sopenharmony_ci	case PARTNER_DEREGISTER:
104362306a36Sopenharmony_ci		ibmvscsis_delete_client_info(vscsi, true);
104462306a36Sopenharmony_ci		if (crq->format == MIGRATED)
104562306a36Sopenharmony_ci			vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE;
104662306a36Sopenharmony_ci		switch (vscsi->state) {
104762306a36Sopenharmony_ci		case NO_QUEUE:
104862306a36Sopenharmony_ci		case ERR_DISCONNECTED:
104962306a36Sopenharmony_ci		case UNDEFINED:
105062306a36Sopenharmony_ci			break;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		case UNCONFIGURING:
105362306a36Sopenharmony_ci			vscsi->flags |= (RESPONSE_Q_DOWN | TRANS_EVENT);
105462306a36Sopenharmony_ci			break;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci		case WAIT_ENABLED:
105762306a36Sopenharmony_ci			break;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci		case WAIT_CONNECTION:
106062306a36Sopenharmony_ci			break;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci		case CONNECTED:
106362306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi, WAIT_IDLE,
106462306a36Sopenharmony_ci						  (RESPONSE_Q_DOWN |
106562306a36Sopenharmony_ci						   TRANS_EVENT));
106662306a36Sopenharmony_ci			break;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci		case SRP_PROCESSING:
106962306a36Sopenharmony_ci			if ((vscsi->debit > 0) ||
107062306a36Sopenharmony_ci			    !list_empty(&vscsi->schedule_q) ||
107162306a36Sopenharmony_ci			    !list_empty(&vscsi->waiting_rsp) ||
107262306a36Sopenharmony_ci			    !list_empty(&vscsi->active_q)) {
107362306a36Sopenharmony_ci				dev_dbg(&vscsi->dev, "debit %d, sched %d, wait %d, active %d\n",
107462306a36Sopenharmony_ci					vscsi->debit,
107562306a36Sopenharmony_ci					(int)list_empty(&vscsi->schedule_q),
107662306a36Sopenharmony_ci					(int)list_empty(&vscsi->waiting_rsp),
107762306a36Sopenharmony_ci					(int)list_empty(&vscsi->active_q));
107862306a36Sopenharmony_ci				dev_warn(&vscsi->dev, "connection lost with outstanding work\n");
107962306a36Sopenharmony_ci			} else {
108062306a36Sopenharmony_ci				dev_dbg(&vscsi->dev, "trans_event: SRP Processing, but no outstanding work\n");
108162306a36Sopenharmony_ci			}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi, WAIT_IDLE,
108462306a36Sopenharmony_ci						  (RESPONSE_Q_DOWN |
108562306a36Sopenharmony_ci						   TRANS_EVENT));
108662306a36Sopenharmony_ci			break;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		case ERR_DISCONNECT:
108962306a36Sopenharmony_ci		case ERR_DISCONNECT_RECONNECT:
109062306a36Sopenharmony_ci		case WAIT_IDLE:
109162306a36Sopenharmony_ci			vscsi->flags |= (RESPONSE_Q_DOWN | TRANS_EVENT);
109262306a36Sopenharmony_ci			break;
109362306a36Sopenharmony_ci		}
109462306a36Sopenharmony_ci		break;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	case PREPARE_FOR_SUSPEND:
109762306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "Prep for Suspend, crq status = 0x%x\n",
109862306a36Sopenharmony_ci			(int)crq->status);
109962306a36Sopenharmony_ci		switch (vscsi->state) {
110062306a36Sopenharmony_ci		case ERR_DISCONNECTED:
110162306a36Sopenharmony_ci		case WAIT_CONNECTION:
110262306a36Sopenharmony_ci		case CONNECTED:
110362306a36Sopenharmony_ci			ibmvscsis_ready_for_suspend(vscsi, false);
110462306a36Sopenharmony_ci			break;
110562306a36Sopenharmony_ci		case SRP_PROCESSING:
110662306a36Sopenharmony_ci			vscsi->resume_state = vscsi->state;
110762306a36Sopenharmony_ci			vscsi->flags |= PREP_FOR_SUSPEND_PENDING;
110862306a36Sopenharmony_ci			if (crq->status == CRQ_ENTRY_OVERWRITTEN)
110962306a36Sopenharmony_ci				vscsi->flags |= PREP_FOR_SUSPEND_OVERWRITE;
111062306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 0);
111162306a36Sopenharmony_ci			break;
111262306a36Sopenharmony_ci		case NO_QUEUE:
111362306a36Sopenharmony_ci		case UNDEFINED:
111462306a36Sopenharmony_ci		case UNCONFIGURING:
111562306a36Sopenharmony_ci		case WAIT_ENABLED:
111662306a36Sopenharmony_ci		case ERR_DISCONNECT:
111762306a36Sopenharmony_ci		case ERR_DISCONNECT_RECONNECT:
111862306a36Sopenharmony_ci		case WAIT_IDLE:
111962306a36Sopenharmony_ci			dev_err(&vscsi->dev, "Invalid state for Prepare for Suspend Trans Event: 0x%x\n",
112062306a36Sopenharmony_ci				vscsi->state);
112162306a36Sopenharmony_ci			break;
112262306a36Sopenharmony_ci		}
112362306a36Sopenharmony_ci		break;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	case RESUME_FROM_SUSP:
112662306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "Resume from Suspend, crq status = 0x%x\n",
112762306a36Sopenharmony_ci			(int)crq->status);
112862306a36Sopenharmony_ci		if (vscsi->flags & PREP_FOR_SUSPEND_PENDING) {
112962306a36Sopenharmony_ci			vscsi->flags |= PREP_FOR_SUSPEND_ABORTED;
113062306a36Sopenharmony_ci		} else {
113162306a36Sopenharmony_ci			if ((crq->status == CRQ_ENTRY_OVERWRITTEN) ||
113262306a36Sopenharmony_ci			    (vscsi->flags & PREP_FOR_SUSPEND_OVERWRITE)) {
113362306a36Sopenharmony_ci				ibmvscsis_post_disconnect(vscsi,
113462306a36Sopenharmony_ci							  ERR_DISCONNECT_RECONNECT,
113562306a36Sopenharmony_ci							  0);
113662306a36Sopenharmony_ci				vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE;
113762306a36Sopenharmony_ci			}
113862306a36Sopenharmony_ci		}
113962306a36Sopenharmony_ci		break;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	default:
114262306a36Sopenharmony_ci		rc = ERROR;
114362306a36Sopenharmony_ci		dev_err(&vscsi->dev, "trans_event: invalid format %d\n",
114462306a36Sopenharmony_ci			(uint)crq->format);
114562306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT,
114662306a36Sopenharmony_ci					  RESPONSE_Q_DOWN);
114762306a36Sopenharmony_ci		break;
114862306a36Sopenharmony_ci	}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	rc = vscsi->flags & SCHEDULE_DISCONNECT;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "Leaving trans_event: flags 0x%x, state 0x%hx, rc %ld\n",
115362306a36Sopenharmony_ci		vscsi->flags, vscsi->state, rc);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	return rc;
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci/**
115962306a36Sopenharmony_ci * ibmvscsis_poll_cmd_q() - Poll Command Queue
116062306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
116162306a36Sopenharmony_ci *
116262306a36Sopenharmony_ci * Called to handle command elements that may have arrived while
116362306a36Sopenharmony_ci * interrupts were disabled.
116462306a36Sopenharmony_ci *
116562306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
116662306a36Sopenharmony_ci *	intr_lock must be held
116762306a36Sopenharmony_ci */
116862306a36Sopenharmony_cistatic void ibmvscsis_poll_cmd_q(struct scsi_info *vscsi)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct viosrp_crq *crq;
117162306a36Sopenharmony_ci	long rc;
117262306a36Sopenharmony_ci	bool ack = true;
117362306a36Sopenharmony_ci	volatile u8 valid;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "poll_cmd_q: flags 0x%x, state 0x%hx, q index %ud\n",
117662306a36Sopenharmony_ci		vscsi->flags, vscsi->state, vscsi->cmd_q.index);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	rc = vscsi->flags & SCHEDULE_DISCONNECT;
117962306a36Sopenharmony_ci	crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index;
118062306a36Sopenharmony_ci	valid = crq->valid;
118162306a36Sopenharmony_ci	dma_rmb();
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	while (valid) {
118462306a36Sopenharmony_cipoll_work:
118562306a36Sopenharmony_ci		vscsi->cmd_q.index =
118662306a36Sopenharmony_ci			(vscsi->cmd_q.index + 1) & vscsi->cmd_q.mask;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		if (!rc) {
118962306a36Sopenharmony_ci			rc = ibmvscsis_parse_command(vscsi, crq);
119062306a36Sopenharmony_ci		} else {
119162306a36Sopenharmony_ci			if ((uint)crq->valid == VALID_TRANS_EVENT) {
119262306a36Sopenharmony_ci				/*
119362306a36Sopenharmony_ci				 * must service the transport layer events even
119462306a36Sopenharmony_ci				 * in an error state, dont break out until all
119562306a36Sopenharmony_ci				 * the consecutive transport events have been
119662306a36Sopenharmony_ci				 * processed
119762306a36Sopenharmony_ci				 */
119862306a36Sopenharmony_ci				rc = ibmvscsis_trans_event(vscsi, crq);
119962306a36Sopenharmony_ci			} else if (vscsi->flags & TRANS_EVENT) {
120062306a36Sopenharmony_ci				/*
120162306a36Sopenharmony_ci				 * if a tranport event has occurred leave
120262306a36Sopenharmony_ci				 * everything but transport events on the queue
120362306a36Sopenharmony_ci				 */
120462306a36Sopenharmony_ci				dev_dbg(&vscsi->dev, "poll_cmd_q, ignoring\n");
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci				/*
120762306a36Sopenharmony_ci				 * need to decrement the queue index so we can
120862306a36Sopenharmony_ci				 * look at the elment again
120962306a36Sopenharmony_ci				 */
121062306a36Sopenharmony_ci				if (vscsi->cmd_q.index)
121162306a36Sopenharmony_ci					vscsi->cmd_q.index -= 1;
121262306a36Sopenharmony_ci				else
121362306a36Sopenharmony_ci					/*
121462306a36Sopenharmony_ci					 * index is at 0 it just wrapped.
121562306a36Sopenharmony_ci					 * have it index last element in q
121662306a36Sopenharmony_ci					 */
121762306a36Sopenharmony_ci					vscsi->cmd_q.index = vscsi->cmd_q.mask;
121862306a36Sopenharmony_ci				break;
121962306a36Sopenharmony_ci			}
122062306a36Sopenharmony_ci		}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		crq->valid = INVALIDATE_CMD_RESP_EL;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci		crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index;
122562306a36Sopenharmony_ci		valid = crq->valid;
122662306a36Sopenharmony_ci		dma_rmb();
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	if (!rc) {
123062306a36Sopenharmony_ci		if (ack) {
123162306a36Sopenharmony_ci			vio_enable_interrupts(vscsi->dma_dev);
123262306a36Sopenharmony_ci			ack = false;
123362306a36Sopenharmony_ci			dev_dbg(&vscsi->dev, "poll_cmd_q, reenabling interrupts\n");
123462306a36Sopenharmony_ci		}
123562306a36Sopenharmony_ci		valid = crq->valid;
123662306a36Sopenharmony_ci		dma_rmb();
123762306a36Sopenharmony_ci		if (valid)
123862306a36Sopenharmony_ci			goto poll_work;
123962306a36Sopenharmony_ci	}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "Leaving poll_cmd_q: rc %ld\n", rc);
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci/**
124562306a36Sopenharmony_ci * ibmvscsis_free_cmd_qs() - Free elements in queue
124662306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
124762306a36Sopenharmony_ci *
124862306a36Sopenharmony_ci * Free all of the elements on all queues that are waiting for
124962306a36Sopenharmony_ci * whatever reason.
125062306a36Sopenharmony_ci *
125162306a36Sopenharmony_ci * PRECONDITION:
125262306a36Sopenharmony_ci *	Called with interrupt lock held
125362306a36Sopenharmony_ci */
125462306a36Sopenharmony_cistatic void ibmvscsis_free_cmd_qs(struct scsi_info *vscsi)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd, *nxt;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "free_cmd_qs: waiting_rsp empty %d, timer starter %d\n",
125962306a36Sopenharmony_ci		(int)list_empty(&vscsi->waiting_rsp),
126062306a36Sopenharmony_ci		vscsi->rsp_q_timer.started);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp, list) {
126362306a36Sopenharmony_ci		list_del(&cmd->list);
126462306a36Sopenharmony_ci		ibmvscsis_free_cmd_resources(vscsi, cmd);
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci/**
126962306a36Sopenharmony_ci * ibmvscsis_get_free_cmd() - Get free command from list
127062306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
127162306a36Sopenharmony_ci *
127262306a36Sopenharmony_ci * Must be called with interrupt lock held.
127362306a36Sopenharmony_ci */
127462306a36Sopenharmony_cistatic struct ibmvscsis_cmd *ibmvscsis_get_free_cmd(struct scsi_info *vscsi)
127562306a36Sopenharmony_ci{
127662306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd = NULL;
127762306a36Sopenharmony_ci	struct iu_entry *iue;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	iue = srp_iu_get(&vscsi->target);
128062306a36Sopenharmony_ci	if (iue) {
128162306a36Sopenharmony_ci		cmd = list_first_entry_or_null(&vscsi->free_cmd,
128262306a36Sopenharmony_ci					       struct ibmvscsis_cmd, list);
128362306a36Sopenharmony_ci		if (cmd) {
128462306a36Sopenharmony_ci			if (cmd->abort_cmd)
128562306a36Sopenharmony_ci				cmd->abort_cmd = NULL;
128662306a36Sopenharmony_ci			cmd->flags &= ~(DELAY_SEND);
128762306a36Sopenharmony_ci			list_del(&cmd->list);
128862306a36Sopenharmony_ci			cmd->iue = iue;
128962306a36Sopenharmony_ci			cmd->type = UNSET_TYPE;
129062306a36Sopenharmony_ci			memset(&cmd->se_cmd, 0, sizeof(cmd->se_cmd));
129162306a36Sopenharmony_ci		} else {
129262306a36Sopenharmony_ci			srp_iu_put(iue);
129362306a36Sopenharmony_ci		}
129462306a36Sopenharmony_ci	}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	return cmd;
129762306a36Sopenharmony_ci}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci/**
130062306a36Sopenharmony_ci * ibmvscsis_adapter_idle() - Helper function to handle idle adapter
130162306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
130262306a36Sopenharmony_ci *
130362306a36Sopenharmony_ci * This function is called when the adapter is idle when the driver
130462306a36Sopenharmony_ci * is attempting to clear an error condition.
130562306a36Sopenharmony_ci * The adapter is considered busy if any of its cmd queues
130662306a36Sopenharmony_ci * are non-empty. This function can be invoked
130762306a36Sopenharmony_ci * from the off level disconnect function.
130862306a36Sopenharmony_ci *
130962306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
131062306a36Sopenharmony_ci *	Process environment called with interrupt lock held
131162306a36Sopenharmony_ci */
131262306a36Sopenharmony_cistatic void ibmvscsis_adapter_idle(struct scsi_info *vscsi)
131362306a36Sopenharmony_ci{
131462306a36Sopenharmony_ci	int free_qs = false;
131562306a36Sopenharmony_ci	long rc = 0;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "adapter_idle: flags 0x%x, state 0x%hx\n",
131862306a36Sopenharmony_ci		vscsi->flags, vscsi->state);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	/* Only need to free qs if we're disconnecting from client */
132162306a36Sopenharmony_ci	if (vscsi->state != WAIT_CONNECTION || vscsi->flags & TRANS_EVENT)
132262306a36Sopenharmony_ci		free_qs = true;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	switch (vscsi->state) {
132562306a36Sopenharmony_ci	case UNCONFIGURING:
132662306a36Sopenharmony_ci		ibmvscsis_free_command_q(vscsi);
132762306a36Sopenharmony_ci		dma_rmb();
132862306a36Sopenharmony_ci		isync();
132962306a36Sopenharmony_ci		if (vscsi->flags & CFG_SLEEPING) {
133062306a36Sopenharmony_ci			vscsi->flags &= ~CFG_SLEEPING;
133162306a36Sopenharmony_ci			complete(&vscsi->unconfig);
133262306a36Sopenharmony_ci		}
133362306a36Sopenharmony_ci		break;
133462306a36Sopenharmony_ci	case ERR_DISCONNECT_RECONNECT:
133562306a36Sopenharmony_ci		ibmvscsis_reset_queue(vscsi);
133662306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "adapter_idle, disc_rec: flags 0x%x\n",
133762306a36Sopenharmony_ci			vscsi->flags);
133862306a36Sopenharmony_ci		break;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	case ERR_DISCONNECT:
134162306a36Sopenharmony_ci		ibmvscsis_free_command_q(vscsi);
134262306a36Sopenharmony_ci		vscsi->flags &= ~(SCHEDULE_DISCONNECT | DISCONNECT_SCHEDULED);
134362306a36Sopenharmony_ci		vscsi->flags |= RESPONSE_Q_DOWN;
134462306a36Sopenharmony_ci		if (vscsi->tport.enabled)
134562306a36Sopenharmony_ci			vscsi->state = ERR_DISCONNECTED;
134662306a36Sopenharmony_ci		else
134762306a36Sopenharmony_ci			vscsi->state = WAIT_ENABLED;
134862306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "adapter_idle, disc: flags 0x%x, state 0x%hx\n",
134962306a36Sopenharmony_ci			vscsi->flags, vscsi->state);
135062306a36Sopenharmony_ci		break;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	case WAIT_IDLE:
135362306a36Sopenharmony_ci		vscsi->rsp_q_timer.timer_pops = 0;
135462306a36Sopenharmony_ci		vscsi->debit = 0;
135562306a36Sopenharmony_ci		vscsi->credit = 0;
135662306a36Sopenharmony_ci		if (vscsi->flags & PREP_FOR_SUSPEND_PENDING) {
135762306a36Sopenharmony_ci			vscsi->state = vscsi->resume_state;
135862306a36Sopenharmony_ci			vscsi->resume_state = 0;
135962306a36Sopenharmony_ci			rc = ibmvscsis_ready_for_suspend(vscsi, true);
136062306a36Sopenharmony_ci			vscsi->flags &= ~DISCONNECT_SCHEDULED;
136162306a36Sopenharmony_ci			if (rc)
136262306a36Sopenharmony_ci				break;
136362306a36Sopenharmony_ci		} else if (vscsi->flags & TRANS_EVENT) {
136462306a36Sopenharmony_ci			vscsi->state = WAIT_CONNECTION;
136562306a36Sopenharmony_ci			vscsi->flags &= PRESERVE_FLAG_FIELDS;
136662306a36Sopenharmony_ci		} else {
136762306a36Sopenharmony_ci			vscsi->state = CONNECTED;
136862306a36Sopenharmony_ci			vscsi->flags &= ~DISCONNECT_SCHEDULED;
136962306a36Sopenharmony_ci		}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "adapter_idle, wait: flags 0x%x, state 0x%hx\n",
137262306a36Sopenharmony_ci			vscsi->flags, vscsi->state);
137362306a36Sopenharmony_ci		ibmvscsis_poll_cmd_q(vscsi);
137462306a36Sopenharmony_ci		break;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	case ERR_DISCONNECTED:
137762306a36Sopenharmony_ci		vscsi->flags &= ~DISCONNECT_SCHEDULED;
137862306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "adapter_idle, disconnected: flags 0x%x, state 0x%hx\n",
137962306a36Sopenharmony_ci			vscsi->flags, vscsi->state);
138062306a36Sopenharmony_ci		break;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	default:
138362306a36Sopenharmony_ci		dev_err(&vscsi->dev, "adapter_idle: in invalid state %d\n",
138462306a36Sopenharmony_ci			vscsi->state);
138562306a36Sopenharmony_ci		break;
138662306a36Sopenharmony_ci	}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	if (free_qs)
138962306a36Sopenharmony_ci		ibmvscsis_free_cmd_qs(vscsi);
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	/*
139262306a36Sopenharmony_ci	 * There is a timing window where we could lose a disconnect request.
139362306a36Sopenharmony_ci	 * The known path to this window occurs during the DISCONNECT_RECONNECT
139462306a36Sopenharmony_ci	 * case above: reset_queue calls free_command_q, which will release the
139562306a36Sopenharmony_ci	 * interrupt lock.  During that time, a new post_disconnect call can be
139662306a36Sopenharmony_ci	 * made with a "more severe" state (DISCONNECT or UNCONFIGURING).
139762306a36Sopenharmony_ci	 * Because the DISCONNECT_SCHEDULED flag is already set, post_disconnect
139862306a36Sopenharmony_ci	 * will only set the new_state.  Now free_command_q reacquires the intr
139962306a36Sopenharmony_ci	 * lock and clears the DISCONNECT_SCHEDULED flag (using PRESERVE_FLAG_
140062306a36Sopenharmony_ci	 * FIELDS), and the disconnect is lost.  This is particularly bad when
140162306a36Sopenharmony_ci	 * the new disconnect was for UNCONFIGURING, since the unconfigure hangs
140262306a36Sopenharmony_ci	 * forever.
140362306a36Sopenharmony_ci	 * Fix is that free command queue sets acr state and acr flags if there
140462306a36Sopenharmony_ci	 * is a change under the lock
140562306a36Sopenharmony_ci	 * note free command queue writes to this state it clears it
140662306a36Sopenharmony_ci	 * before releasing the lock, different drivers call the free command
140762306a36Sopenharmony_ci	 * queue different times so dont initialize above
140862306a36Sopenharmony_ci	 */
140962306a36Sopenharmony_ci	if (vscsi->phyp_acr_state != 0)	{
141062306a36Sopenharmony_ci		/*
141162306a36Sopenharmony_ci		 * set any bits in flags that may have been cleared by
141262306a36Sopenharmony_ci		 * a call to free command queue in switch statement
141362306a36Sopenharmony_ci		 * or reset queue
141462306a36Sopenharmony_ci		 */
141562306a36Sopenharmony_ci		vscsi->flags |= vscsi->phyp_acr_flags;
141662306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, vscsi->phyp_acr_state, 0);
141762306a36Sopenharmony_ci		vscsi->phyp_acr_state = 0;
141862306a36Sopenharmony_ci		vscsi->phyp_acr_flags = 0;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "adapter_idle: flags 0x%x, state 0x%hx, acr_flags 0x%x, acr_state 0x%hx\n",
142162306a36Sopenharmony_ci			vscsi->flags, vscsi->state, vscsi->phyp_acr_flags,
142262306a36Sopenharmony_ci			vscsi->phyp_acr_state);
142362306a36Sopenharmony_ci	}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "Leaving adapter_idle: flags 0x%x, state 0x%hx, new_state 0x%x\n",
142662306a36Sopenharmony_ci		vscsi->flags, vscsi->state, vscsi->new_state);
142762306a36Sopenharmony_ci}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci/**
143062306a36Sopenharmony_ci * ibmvscsis_copy_crq_packet() - Copy CRQ Packet
143162306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
143262306a36Sopenharmony_ci * @cmd:	Pointer to command element to use to process the request
143362306a36Sopenharmony_ci * @crq:	Pointer to CRQ entry containing the request
143462306a36Sopenharmony_ci *
143562306a36Sopenharmony_ci * Copy the srp information unit from the hosted
143662306a36Sopenharmony_ci * partition using remote dma
143762306a36Sopenharmony_ci *
143862306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
143962306a36Sopenharmony_ci *	Interrupt, interrupt lock held
144062306a36Sopenharmony_ci */
144162306a36Sopenharmony_cistatic long ibmvscsis_copy_crq_packet(struct scsi_info *vscsi,
144262306a36Sopenharmony_ci				      struct ibmvscsis_cmd *cmd,
144362306a36Sopenharmony_ci				      struct viosrp_crq *crq)
144462306a36Sopenharmony_ci{
144562306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
144662306a36Sopenharmony_ci	long rc = 0;
144762306a36Sopenharmony_ci	u16 len;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	len = be16_to_cpu(crq->IU_length);
145062306a36Sopenharmony_ci	if ((len > SRP_MAX_IU_LEN) || (len == 0)) {
145162306a36Sopenharmony_ci		dev_err(&vscsi->dev, "copy_crq: Invalid len %d passed", len);
145262306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
145362306a36Sopenharmony_ci		return SRP_VIOLATION;
145462306a36Sopenharmony_ci	}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	rc = h_copy_rdma(len, vscsi->dds.window[REMOTE].liobn,
145762306a36Sopenharmony_ci			 be64_to_cpu(crq->IU_data_ptr),
145862306a36Sopenharmony_ci			 vscsi->dds.window[LOCAL].liobn, iue->sbuf->dma);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	switch (rc) {
146162306a36Sopenharmony_ci	case H_SUCCESS:
146262306a36Sopenharmony_ci		cmd->init_time = mftb();
146362306a36Sopenharmony_ci		iue->remote_token = crq->IU_data_ptr;
146462306a36Sopenharmony_ci		iue->iu_len = len;
146562306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "copy_crq: ioba 0x%llx, init_time 0x%llx\n",
146662306a36Sopenharmony_ci			be64_to_cpu(crq->IU_data_ptr), cmd->init_time);
146762306a36Sopenharmony_ci		break;
146862306a36Sopenharmony_ci	case H_PERMISSION:
146962306a36Sopenharmony_ci		if (connection_broken(vscsi))
147062306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi,
147162306a36Sopenharmony_ci						  ERR_DISCONNECT_RECONNECT,
147262306a36Sopenharmony_ci						  (RESPONSE_Q_DOWN |
147362306a36Sopenharmony_ci						   CLIENT_FAILED));
147462306a36Sopenharmony_ci		else
147562306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi,
147662306a36Sopenharmony_ci						  ERR_DISCONNECT_RECONNECT, 0);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci		dev_err(&vscsi->dev, "copy_crq: h_copy_rdma failed, rc %ld\n",
147962306a36Sopenharmony_ci			rc);
148062306a36Sopenharmony_ci		break;
148162306a36Sopenharmony_ci	case H_DEST_PARM:
148262306a36Sopenharmony_ci	case H_SOURCE_PARM:
148362306a36Sopenharmony_ci	default:
148462306a36Sopenharmony_ci		dev_err(&vscsi->dev, "copy_crq: h_copy_rdma failed, rc %ld\n",
148562306a36Sopenharmony_ci			rc);
148662306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
148762306a36Sopenharmony_ci		break;
148862306a36Sopenharmony_ci	}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	return rc;
149162306a36Sopenharmony_ci}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci/**
149462306a36Sopenharmony_ci * ibmvscsis_adapter_info - Service an Adapter Info MAnagement Data gram
149562306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
149662306a36Sopenharmony_ci * @iue:	Information Unit containing the Adapter Info MAD request
149762306a36Sopenharmony_ci *
149862306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
149962306a36Sopenharmony_ci *	Interrupt adapter lock is held
150062306a36Sopenharmony_ci */
150162306a36Sopenharmony_cistatic long ibmvscsis_adapter_info(struct scsi_info *vscsi,
150262306a36Sopenharmony_ci				   struct iu_entry *iue)
150362306a36Sopenharmony_ci{
150462306a36Sopenharmony_ci	struct viosrp_adapter_info *mad = &vio_iu(iue)->mad.adapter_info;
150562306a36Sopenharmony_ci	struct mad_adapter_info_data *info;
150662306a36Sopenharmony_ci	uint flag_bits = 0;
150762306a36Sopenharmony_ci	dma_addr_t token;
150862306a36Sopenharmony_ci	long rc;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	mad->common.status = cpu_to_be16(VIOSRP_MAD_SUCCESS);
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	if (be16_to_cpu(mad->common.length) > sizeof(*info)) {
151362306a36Sopenharmony_ci		mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED);
151462306a36Sopenharmony_ci		return 0;
151562306a36Sopenharmony_ci	}
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	info = dma_alloc_coherent(&vscsi->dma_dev->dev, sizeof(*info), &token,
151862306a36Sopenharmony_ci				  GFP_ATOMIC);
151962306a36Sopenharmony_ci	if (!info) {
152062306a36Sopenharmony_ci		dev_err(&vscsi->dev, "bad dma_alloc_coherent %p\n",
152162306a36Sopenharmony_ci			iue->target);
152262306a36Sopenharmony_ci		mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED);
152362306a36Sopenharmony_ci		return 0;
152462306a36Sopenharmony_ci	}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	/* Get remote info */
152762306a36Sopenharmony_ci	rc = h_copy_rdma(be16_to_cpu(mad->common.length),
152862306a36Sopenharmony_ci			 vscsi->dds.window[REMOTE].liobn,
152962306a36Sopenharmony_ci			 be64_to_cpu(mad->buffer),
153062306a36Sopenharmony_ci			 vscsi->dds.window[LOCAL].liobn, token);
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	if (rc != H_SUCCESS) {
153362306a36Sopenharmony_ci		if (rc == H_PERMISSION) {
153462306a36Sopenharmony_ci			if (connection_broken(vscsi))
153562306a36Sopenharmony_ci				flag_bits = (RESPONSE_Q_DOWN | CLIENT_FAILED);
153662306a36Sopenharmony_ci		}
153762306a36Sopenharmony_ci		dev_warn(&vscsi->dev, "adapter_info: h_copy_rdma from client failed, rc %ld\n",
153862306a36Sopenharmony_ci			 rc);
153962306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "adapter_info: ioba 0x%llx, flags 0x%x, flag_bits 0x%x\n",
154062306a36Sopenharmony_ci			be64_to_cpu(mad->buffer), vscsi->flags, flag_bits);
154162306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT,
154262306a36Sopenharmony_ci					  flag_bits);
154362306a36Sopenharmony_ci		goto free_dma;
154462306a36Sopenharmony_ci	}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	/*
154762306a36Sopenharmony_ci	 * Copy client info, but ignore partition number, which we
154862306a36Sopenharmony_ci	 * already got from phyp - unless we failed to get it from
154962306a36Sopenharmony_ci	 * phyp (e.g. if we're running on a p5 system).
155062306a36Sopenharmony_ci	 */
155162306a36Sopenharmony_ci	if (vscsi->client_data.partition_number == 0)
155262306a36Sopenharmony_ci		vscsi->client_data.partition_number =
155362306a36Sopenharmony_ci			be32_to_cpu(info->partition_number);
155462306a36Sopenharmony_ci	strncpy(vscsi->client_data.srp_version, info->srp_version,
155562306a36Sopenharmony_ci		sizeof(vscsi->client_data.srp_version));
155662306a36Sopenharmony_ci	strncpy(vscsi->client_data.partition_name, info->partition_name,
155762306a36Sopenharmony_ci		sizeof(vscsi->client_data.partition_name));
155862306a36Sopenharmony_ci	vscsi->client_data.mad_version = be32_to_cpu(info->mad_version);
155962306a36Sopenharmony_ci	vscsi->client_data.os_type = be32_to_cpu(info->os_type);
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	/* Copy our info */
156262306a36Sopenharmony_ci	strncpy(info->srp_version, SRP_VERSION,
156362306a36Sopenharmony_ci		sizeof(info->srp_version));
156462306a36Sopenharmony_ci	strncpy(info->partition_name, vscsi->dds.partition_name,
156562306a36Sopenharmony_ci		sizeof(info->partition_name));
156662306a36Sopenharmony_ci	info->partition_number = cpu_to_be32(vscsi->dds.partition_num);
156762306a36Sopenharmony_ci	info->mad_version = cpu_to_be32(MAD_VERSION_1);
156862306a36Sopenharmony_ci	info->os_type = cpu_to_be32(LINUX);
156962306a36Sopenharmony_ci	memset(&info->port_max_txu[0], 0, sizeof(info->port_max_txu));
157062306a36Sopenharmony_ci	info->port_max_txu[0] = cpu_to_be32(MAX_TXU);
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	dma_wmb();
157362306a36Sopenharmony_ci	rc = h_copy_rdma(sizeof(*info), vscsi->dds.window[LOCAL].liobn,
157462306a36Sopenharmony_ci			 token, vscsi->dds.window[REMOTE].liobn,
157562306a36Sopenharmony_ci			 be64_to_cpu(mad->buffer));
157662306a36Sopenharmony_ci	switch (rc) {
157762306a36Sopenharmony_ci	case H_SUCCESS:
157862306a36Sopenharmony_ci		break;
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	case H_SOURCE_PARM:
158162306a36Sopenharmony_ci	case H_DEST_PARM:
158262306a36Sopenharmony_ci	case H_PERMISSION:
158362306a36Sopenharmony_ci		if (connection_broken(vscsi))
158462306a36Sopenharmony_ci			flag_bits = (RESPONSE_Q_DOWN | CLIENT_FAILED);
158562306a36Sopenharmony_ci		fallthrough;
158662306a36Sopenharmony_ci	default:
158762306a36Sopenharmony_ci		dev_err(&vscsi->dev, "adapter_info: h_copy_rdma to client failed, rc %ld\n",
158862306a36Sopenharmony_ci			rc);
158962306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi,
159062306a36Sopenharmony_ci					  ERR_DISCONNECT_RECONNECT,
159162306a36Sopenharmony_ci					  flag_bits);
159262306a36Sopenharmony_ci		break;
159362306a36Sopenharmony_ci	}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_cifree_dma:
159662306a36Sopenharmony_ci	dma_free_coherent(&vscsi->dma_dev->dev, sizeof(*info), info, token);
159762306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "Leaving adapter_info, rc %ld\n", rc);
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	return rc;
160062306a36Sopenharmony_ci}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci/**
160362306a36Sopenharmony_ci * ibmvscsis_cap_mad() - Service a Capabilities MAnagement Data gram
160462306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
160562306a36Sopenharmony_ci * @iue:	Information Unit containing the Capabilities MAD request
160662306a36Sopenharmony_ci *
160762306a36Sopenharmony_ci * NOTE: if you return an error from this routine you must be
160862306a36Sopenharmony_ci * disconnecting or you will cause a hang
160962306a36Sopenharmony_ci *
161062306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
161162306a36Sopenharmony_ci *	Interrupt called with adapter lock held
161262306a36Sopenharmony_ci */
161362306a36Sopenharmony_cistatic int ibmvscsis_cap_mad(struct scsi_info *vscsi, struct iu_entry *iue)
161462306a36Sopenharmony_ci{
161562306a36Sopenharmony_ci	struct viosrp_capabilities *mad = &vio_iu(iue)->mad.capabilities;
161662306a36Sopenharmony_ci	struct capabilities *cap;
161762306a36Sopenharmony_ci	struct mad_capability_common *common;
161862306a36Sopenharmony_ci	dma_addr_t token;
161962306a36Sopenharmony_ci	u16 olen, len, status, min_len, cap_len;
162062306a36Sopenharmony_ci	u32 flag;
162162306a36Sopenharmony_ci	uint flag_bits = 0;
162262306a36Sopenharmony_ci	long rc = 0;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	olen = be16_to_cpu(mad->common.length);
162562306a36Sopenharmony_ci	/*
162662306a36Sopenharmony_ci	 * struct capabilities hardcodes a couple capabilities after the
162762306a36Sopenharmony_ci	 * header, but the capabilities can actually be in any order.
162862306a36Sopenharmony_ci	 */
162962306a36Sopenharmony_ci	min_len = offsetof(struct capabilities, migration);
163062306a36Sopenharmony_ci	if ((olen < min_len) || (olen > PAGE_SIZE)) {
163162306a36Sopenharmony_ci		dev_warn(&vscsi->dev, "cap_mad: invalid len %d\n", olen);
163262306a36Sopenharmony_ci		mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED);
163362306a36Sopenharmony_ci		return 0;
163462306a36Sopenharmony_ci	}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	cap = dma_alloc_coherent(&vscsi->dma_dev->dev, olen, &token,
163762306a36Sopenharmony_ci				 GFP_ATOMIC);
163862306a36Sopenharmony_ci	if (!cap) {
163962306a36Sopenharmony_ci		dev_err(&vscsi->dev, "bad dma_alloc_coherent %p\n",
164062306a36Sopenharmony_ci			iue->target);
164162306a36Sopenharmony_ci		mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED);
164262306a36Sopenharmony_ci		return 0;
164362306a36Sopenharmony_ci	}
164462306a36Sopenharmony_ci	rc = h_copy_rdma(olen, vscsi->dds.window[REMOTE].liobn,
164562306a36Sopenharmony_ci			 be64_to_cpu(mad->buffer),
164662306a36Sopenharmony_ci			 vscsi->dds.window[LOCAL].liobn, token);
164762306a36Sopenharmony_ci	if (rc == H_SUCCESS) {
164862306a36Sopenharmony_ci		strncpy(cap->name, dev_name(&vscsi->dma_dev->dev),
164962306a36Sopenharmony_ci			SRP_MAX_LOC_LEN);
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci		len = olen - min_len;
165262306a36Sopenharmony_ci		status = VIOSRP_MAD_SUCCESS;
165362306a36Sopenharmony_ci		common = (struct mad_capability_common *)&cap->migration;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci		while ((len > 0) && (status == VIOSRP_MAD_SUCCESS) && !rc) {
165662306a36Sopenharmony_ci			dev_dbg(&vscsi->dev, "cap_mad: len left %hd, cap type %d, cap len %hd\n",
165762306a36Sopenharmony_ci				len, be32_to_cpu(common->cap_type),
165862306a36Sopenharmony_ci				be16_to_cpu(common->length));
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci			cap_len = be16_to_cpu(common->length);
166162306a36Sopenharmony_ci			if (cap_len > len) {
166262306a36Sopenharmony_ci				dev_err(&vscsi->dev, "cap_mad: cap len mismatch with total len\n");
166362306a36Sopenharmony_ci				status = VIOSRP_MAD_FAILED;
166462306a36Sopenharmony_ci				break;
166562306a36Sopenharmony_ci			}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci			if (cap_len == 0) {
166862306a36Sopenharmony_ci				dev_err(&vscsi->dev, "cap_mad: cap len is 0\n");
166962306a36Sopenharmony_ci				status = VIOSRP_MAD_FAILED;
167062306a36Sopenharmony_ci				break;
167162306a36Sopenharmony_ci			}
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci			switch (common->cap_type) {
167462306a36Sopenharmony_ci			default:
167562306a36Sopenharmony_ci				dev_dbg(&vscsi->dev, "cap_mad: unsupported capability\n");
167662306a36Sopenharmony_ci				common->server_support = 0;
167762306a36Sopenharmony_ci				flag = cpu_to_be32((u32)CAP_LIST_SUPPORTED);
167862306a36Sopenharmony_ci				cap->flags &= ~flag;
167962306a36Sopenharmony_ci				break;
168062306a36Sopenharmony_ci			}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci			len = len - cap_len;
168362306a36Sopenharmony_ci			common = (struct mad_capability_common *)
168462306a36Sopenharmony_ci				((char *)common + cap_len);
168562306a36Sopenharmony_ci		}
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci		mad->common.status = cpu_to_be16(status);
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci		dma_wmb();
169062306a36Sopenharmony_ci		rc = h_copy_rdma(olen, vscsi->dds.window[LOCAL].liobn, token,
169162306a36Sopenharmony_ci				 vscsi->dds.window[REMOTE].liobn,
169262306a36Sopenharmony_ci				 be64_to_cpu(mad->buffer));
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci		if (rc != H_SUCCESS) {
169562306a36Sopenharmony_ci			dev_dbg(&vscsi->dev, "cap_mad: failed to copy to client, rc %ld\n",
169662306a36Sopenharmony_ci				rc);
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci			if (rc == H_PERMISSION) {
169962306a36Sopenharmony_ci				if (connection_broken(vscsi))
170062306a36Sopenharmony_ci					flag_bits = (RESPONSE_Q_DOWN |
170162306a36Sopenharmony_ci						     CLIENT_FAILED);
170262306a36Sopenharmony_ci			}
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci			dev_warn(&vscsi->dev, "cap_mad: error copying data to client, rc %ld\n",
170562306a36Sopenharmony_ci				 rc);
170662306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi,
170762306a36Sopenharmony_ci						  ERR_DISCONNECT_RECONNECT,
170862306a36Sopenharmony_ci						  flag_bits);
170962306a36Sopenharmony_ci		}
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	dma_free_coherent(&vscsi->dma_dev->dev, olen, cap, token);
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "Leaving cap_mad, rc %ld, client_cap 0x%x\n",
171562306a36Sopenharmony_ci		rc, vscsi->client_cap);
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	return rc;
171862306a36Sopenharmony_ci}
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci/**
172162306a36Sopenharmony_ci * ibmvscsis_process_mad() - Service a MAnagement Data gram
172262306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
172362306a36Sopenharmony_ci * @iue:	Information Unit containing the MAD request
172462306a36Sopenharmony_ci *
172562306a36Sopenharmony_ci * Must be called with interrupt lock held.
172662306a36Sopenharmony_ci */
172762306a36Sopenharmony_cistatic long ibmvscsis_process_mad(struct scsi_info *vscsi, struct iu_entry *iue)
172862306a36Sopenharmony_ci{
172962306a36Sopenharmony_ci	struct mad_common *mad = (struct mad_common *)&vio_iu(iue)->mad;
173062306a36Sopenharmony_ci	struct viosrp_empty_iu *empty;
173162306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	switch (be32_to_cpu(mad->type)) {
173462306a36Sopenharmony_ci	case VIOSRP_EMPTY_IU_TYPE:
173562306a36Sopenharmony_ci		empty = &vio_iu(iue)->mad.empty_iu;
173662306a36Sopenharmony_ci		vscsi->empty_iu_id = be64_to_cpu(empty->buffer);
173762306a36Sopenharmony_ci		vscsi->empty_iu_tag = be64_to_cpu(empty->common.tag);
173862306a36Sopenharmony_ci		mad->status = cpu_to_be16(VIOSRP_MAD_SUCCESS);
173962306a36Sopenharmony_ci		break;
174062306a36Sopenharmony_ci	case VIOSRP_ADAPTER_INFO_TYPE:
174162306a36Sopenharmony_ci		rc = ibmvscsis_adapter_info(vscsi, iue);
174262306a36Sopenharmony_ci		break;
174362306a36Sopenharmony_ci	case VIOSRP_CAPABILITIES_TYPE:
174462306a36Sopenharmony_ci		rc = ibmvscsis_cap_mad(vscsi, iue);
174562306a36Sopenharmony_ci		break;
174662306a36Sopenharmony_ci	case VIOSRP_ENABLE_FAST_FAIL:
174762306a36Sopenharmony_ci		if (vscsi->state == CONNECTED) {
174862306a36Sopenharmony_ci			vscsi->fast_fail = true;
174962306a36Sopenharmony_ci			mad->status = cpu_to_be16(VIOSRP_MAD_SUCCESS);
175062306a36Sopenharmony_ci		} else {
175162306a36Sopenharmony_ci			dev_warn(&vscsi->dev, "fast fail mad sent after login\n");
175262306a36Sopenharmony_ci			mad->status = cpu_to_be16(VIOSRP_MAD_FAILED);
175362306a36Sopenharmony_ci		}
175462306a36Sopenharmony_ci		break;
175562306a36Sopenharmony_ci	default:
175662306a36Sopenharmony_ci		mad->status = cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED);
175762306a36Sopenharmony_ci		break;
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	return rc;
176162306a36Sopenharmony_ci}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci/**
176462306a36Sopenharmony_ci * srp_snd_msg_failed() - Handle an error when sending a response
176562306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
176662306a36Sopenharmony_ci * @rc:		The return code from the h_send_crq command
176762306a36Sopenharmony_ci *
176862306a36Sopenharmony_ci * Must be called with interrupt lock held.
176962306a36Sopenharmony_ci */
177062306a36Sopenharmony_cistatic void srp_snd_msg_failed(struct scsi_info *vscsi, long rc)
177162306a36Sopenharmony_ci{
177262306a36Sopenharmony_ci	ktime_t kt;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	if (rc != H_DROPPED) {
177562306a36Sopenharmony_ci		ibmvscsis_free_cmd_qs(vscsi);
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci		if (rc == H_CLOSED)
177862306a36Sopenharmony_ci			vscsi->flags |= CLIENT_FAILED;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci		/* don't flag the same problem multiple times */
178162306a36Sopenharmony_ci		if (!(vscsi->flags & RESPONSE_Q_DOWN)) {
178262306a36Sopenharmony_ci			vscsi->flags |= RESPONSE_Q_DOWN;
178362306a36Sopenharmony_ci			if (!(vscsi->state & (ERR_DISCONNECT |
178462306a36Sopenharmony_ci					      ERR_DISCONNECT_RECONNECT |
178562306a36Sopenharmony_ci					      ERR_DISCONNECTED | UNDEFINED))) {
178662306a36Sopenharmony_ci				dev_err(&vscsi->dev, "snd_msg_failed: setting RESPONSE_Q_DOWN, state 0x%hx, flags 0x%x, rc %ld\n",
178762306a36Sopenharmony_ci					vscsi->state, vscsi->flags, rc);
178862306a36Sopenharmony_ci			}
178962306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi,
179062306a36Sopenharmony_ci						  ERR_DISCONNECT_RECONNECT, 0);
179162306a36Sopenharmony_ci		}
179262306a36Sopenharmony_ci		return;
179362306a36Sopenharmony_ci	}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	/*
179662306a36Sopenharmony_ci	 * The response queue is full.
179762306a36Sopenharmony_ci	 * If the server is processing SRP requests, i.e.
179862306a36Sopenharmony_ci	 * the client has successfully done an
179962306a36Sopenharmony_ci	 * SRP_LOGIN, then it will wait forever for room in
180062306a36Sopenharmony_ci	 * the queue.  However if the system admin
180162306a36Sopenharmony_ci	 * is attempting to unconfigure the server then one
180262306a36Sopenharmony_ci	 * or more children will be in a state where
180362306a36Sopenharmony_ci	 * they are being removed. So if there is even one
180462306a36Sopenharmony_ci	 * child being removed then the driver assumes
180562306a36Sopenharmony_ci	 * the system admin is attempting to break the
180662306a36Sopenharmony_ci	 * connection with the client and MAX_TIMER_POPS
180762306a36Sopenharmony_ci	 * is honored.
180862306a36Sopenharmony_ci	 */
180962306a36Sopenharmony_ci	if ((vscsi->rsp_q_timer.timer_pops < MAX_TIMER_POPS) ||
181062306a36Sopenharmony_ci	    (vscsi->state == SRP_PROCESSING)) {
181162306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "snd_msg_failed: response queue full, flags 0x%x, timer started %d, pops %d\n",
181262306a36Sopenharmony_ci			vscsi->flags, (int)vscsi->rsp_q_timer.started,
181362306a36Sopenharmony_ci			vscsi->rsp_q_timer.timer_pops);
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci		/*
181662306a36Sopenharmony_ci		 * Check if the timer is running; if it
181762306a36Sopenharmony_ci		 * is not then start it up.
181862306a36Sopenharmony_ci		 */
181962306a36Sopenharmony_ci		if (!vscsi->rsp_q_timer.started) {
182062306a36Sopenharmony_ci			if (vscsi->rsp_q_timer.timer_pops <
182162306a36Sopenharmony_ci			    MAX_TIMER_POPS) {
182262306a36Sopenharmony_ci				kt = WAIT_NANO_SECONDS;
182362306a36Sopenharmony_ci			} else {
182462306a36Sopenharmony_ci				/*
182562306a36Sopenharmony_ci				 * slide the timeslice if the maximum
182662306a36Sopenharmony_ci				 * timer pops have already happened
182762306a36Sopenharmony_ci				 */
182862306a36Sopenharmony_ci				kt = ktime_set(WAIT_SECONDS, 0);
182962306a36Sopenharmony_ci			}
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci			vscsi->rsp_q_timer.started = true;
183262306a36Sopenharmony_ci			hrtimer_start(&vscsi->rsp_q_timer.timer, kt,
183362306a36Sopenharmony_ci				      HRTIMER_MODE_REL);
183462306a36Sopenharmony_ci		}
183562306a36Sopenharmony_ci	} else {
183662306a36Sopenharmony_ci		/*
183762306a36Sopenharmony_ci		 * TBD: Do we need to worry about this? Need to get
183862306a36Sopenharmony_ci		 *      remove working.
183962306a36Sopenharmony_ci		 */
184062306a36Sopenharmony_ci		/*
184162306a36Sopenharmony_ci		 * waited a long time and it appears the system admin
184262306a36Sopenharmony_ci		 * is bring this driver down
184362306a36Sopenharmony_ci		 */
184462306a36Sopenharmony_ci		vscsi->flags |= RESPONSE_Q_DOWN;
184562306a36Sopenharmony_ci		ibmvscsis_free_cmd_qs(vscsi);
184662306a36Sopenharmony_ci		/*
184762306a36Sopenharmony_ci		 * if the driver is already attempting to disconnect
184862306a36Sopenharmony_ci		 * from the client and has already logged an error
184962306a36Sopenharmony_ci		 * trace this event but don't put it in the error log
185062306a36Sopenharmony_ci		 */
185162306a36Sopenharmony_ci		if (!(vscsi->state & (ERR_DISCONNECT |
185262306a36Sopenharmony_ci				      ERR_DISCONNECT_RECONNECT |
185362306a36Sopenharmony_ci				      ERR_DISCONNECTED | UNDEFINED))) {
185462306a36Sopenharmony_ci			dev_err(&vscsi->dev, "client crq full too long\n");
185562306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi,
185662306a36Sopenharmony_ci						  ERR_DISCONNECT_RECONNECT,
185762306a36Sopenharmony_ci						  0);
185862306a36Sopenharmony_ci		}
185962306a36Sopenharmony_ci	}
186062306a36Sopenharmony_ci}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci/**
186362306a36Sopenharmony_ci * ibmvscsis_send_messages() - Send a Response
186462306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
186562306a36Sopenharmony_ci *
186662306a36Sopenharmony_ci * Send a response, first checking the waiting queue. Responses are
186762306a36Sopenharmony_ci * sent in order they are received. If the response cannot be sent,
186862306a36Sopenharmony_ci * because the client queue is full, it stays on the waiting queue.
186962306a36Sopenharmony_ci *
187062306a36Sopenharmony_ci * PRECONDITION:
187162306a36Sopenharmony_ci *	Called with interrupt lock held
187262306a36Sopenharmony_ci */
187362306a36Sopenharmony_cistatic void ibmvscsis_send_messages(struct scsi_info *vscsi)
187462306a36Sopenharmony_ci{
187562306a36Sopenharmony_ci	struct viosrp_crq empty_crq = { };
187662306a36Sopenharmony_ci	struct viosrp_crq *crq = &empty_crq;
187762306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd, *nxt;
187862306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
187962306a36Sopenharmony_ci	bool retry = false;
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	if (!(vscsi->flags & RESPONSE_Q_DOWN)) {
188262306a36Sopenharmony_ci		do {
188362306a36Sopenharmony_ci			retry = false;
188462306a36Sopenharmony_ci			list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp,
188562306a36Sopenharmony_ci						 list) {
188662306a36Sopenharmony_ci				/*
188762306a36Sopenharmony_ci				 * Check to make sure abort cmd gets processed
188862306a36Sopenharmony_ci				 * prior to the abort tmr cmd
188962306a36Sopenharmony_ci				 */
189062306a36Sopenharmony_ci				if (cmd->flags & DELAY_SEND)
189162306a36Sopenharmony_ci					continue;
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci				if (cmd->abort_cmd) {
189462306a36Sopenharmony_ci					retry = true;
189562306a36Sopenharmony_ci					cmd->abort_cmd->flags &= ~(DELAY_SEND);
189662306a36Sopenharmony_ci					cmd->abort_cmd = NULL;
189762306a36Sopenharmony_ci				}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci				/*
190062306a36Sopenharmony_ci				 * If CMD_T_ABORTED w/o CMD_T_TAS scenarios and
190162306a36Sopenharmony_ci				 * the case where LIO issued a
190262306a36Sopenharmony_ci				 * ABORT_TASK: Sending TMR_TASK_DOES_NOT_EXIST
190362306a36Sopenharmony_ci				 * case then we dont send a response, since it
190462306a36Sopenharmony_ci				 * was already done.
190562306a36Sopenharmony_ci				 */
190662306a36Sopenharmony_ci				if (cmd->se_cmd.transport_state & CMD_T_ABORTED &&
190762306a36Sopenharmony_ci				    !(cmd->se_cmd.transport_state & CMD_T_TAS)) {
190862306a36Sopenharmony_ci					list_del(&cmd->list);
190962306a36Sopenharmony_ci					ibmvscsis_free_cmd_resources(vscsi,
191062306a36Sopenharmony_ci								     cmd);
191162306a36Sopenharmony_ci					/*
191262306a36Sopenharmony_ci					 * With a successfully aborted op
191362306a36Sopenharmony_ci					 * through LIO we want to increment the
191462306a36Sopenharmony_ci					 * the vscsi credit so that when we dont
191562306a36Sopenharmony_ci					 * send a rsp to the original scsi abort
191662306a36Sopenharmony_ci					 * op (h_send_crq), but the tm rsp to
191762306a36Sopenharmony_ci					 * the abort is sent, the credit is
191862306a36Sopenharmony_ci					 * correctly sent with the abort tm rsp.
191962306a36Sopenharmony_ci					 * We would need 1 for the abort tm rsp
192062306a36Sopenharmony_ci					 * and 1 credit for the aborted scsi op.
192162306a36Sopenharmony_ci					 * Thus we need to increment here.
192262306a36Sopenharmony_ci					 * Also we want to increment the credit
192362306a36Sopenharmony_ci					 * here because we want to make sure
192462306a36Sopenharmony_ci					 * cmd is actually released first
192562306a36Sopenharmony_ci					 * otherwise the client will think it
192662306a36Sopenharmony_ci					 * it can send a new cmd, and we could
192762306a36Sopenharmony_ci					 * find ourselves short of cmd elements.
192862306a36Sopenharmony_ci					 */
192962306a36Sopenharmony_ci					vscsi->credit += 1;
193062306a36Sopenharmony_ci				} else {
193162306a36Sopenharmony_ci					crq->valid = VALID_CMD_RESP_EL;
193262306a36Sopenharmony_ci					crq->format = cmd->rsp.format;
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci					if (cmd->flags & CMD_FAST_FAIL)
193562306a36Sopenharmony_ci						crq->status = VIOSRP_ADAPTER_FAIL;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci					crq->IU_length = cpu_to_be16(cmd->rsp.len);
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci					rc = h_send_crq(vscsi->dma_dev->unit_address,
194062306a36Sopenharmony_ci							be64_to_cpu(crq->high),
194162306a36Sopenharmony_ci							be64_to_cpu(cmd->rsp.tag));
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci					dev_dbg(&vscsi->dev, "send_messages: cmd %p, tag 0x%llx, rc %ld\n",
194462306a36Sopenharmony_ci						cmd, be64_to_cpu(cmd->rsp.tag),
194562306a36Sopenharmony_ci						rc);
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci					/* if all ok free up the command
194862306a36Sopenharmony_ci					 * element resources
194962306a36Sopenharmony_ci					 */
195062306a36Sopenharmony_ci					if (rc == H_SUCCESS) {
195162306a36Sopenharmony_ci						/* some movement has occurred */
195262306a36Sopenharmony_ci						vscsi->rsp_q_timer.timer_pops = 0;
195362306a36Sopenharmony_ci						list_del(&cmd->list);
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci						ibmvscsis_free_cmd_resources(vscsi,
195662306a36Sopenharmony_ci									     cmd);
195762306a36Sopenharmony_ci					} else {
195862306a36Sopenharmony_ci						srp_snd_msg_failed(vscsi, rc);
195962306a36Sopenharmony_ci						break;
196062306a36Sopenharmony_ci					}
196162306a36Sopenharmony_ci				}
196262306a36Sopenharmony_ci			}
196362306a36Sopenharmony_ci		} while (retry);
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci		if (!rc) {
196662306a36Sopenharmony_ci			/*
196762306a36Sopenharmony_ci			 * The timer could pop with the queue empty.  If
196862306a36Sopenharmony_ci			 * this happens, rc will always indicate a
196962306a36Sopenharmony_ci			 * success; clear the pop count.
197062306a36Sopenharmony_ci			 */
197162306a36Sopenharmony_ci			vscsi->rsp_q_timer.timer_pops = 0;
197262306a36Sopenharmony_ci		}
197362306a36Sopenharmony_ci	} else {
197462306a36Sopenharmony_ci		ibmvscsis_free_cmd_qs(vscsi);
197562306a36Sopenharmony_ci	}
197662306a36Sopenharmony_ci}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci/* Called with intr lock held */
197962306a36Sopenharmony_cistatic void ibmvscsis_send_mad_resp(struct scsi_info *vscsi,
198062306a36Sopenharmony_ci				    struct ibmvscsis_cmd *cmd,
198162306a36Sopenharmony_ci				    struct viosrp_crq *crq)
198262306a36Sopenharmony_ci{
198362306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
198462306a36Sopenharmony_ci	struct mad_common *mad = (struct mad_common *)&vio_iu(iue)->mad;
198562306a36Sopenharmony_ci	uint flag_bits = 0;
198662306a36Sopenharmony_ci	long rc;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	dma_wmb();
198962306a36Sopenharmony_ci	rc = h_copy_rdma(sizeof(struct mad_common),
199062306a36Sopenharmony_ci			 vscsi->dds.window[LOCAL].liobn, iue->sbuf->dma,
199162306a36Sopenharmony_ci			 vscsi->dds.window[REMOTE].liobn,
199262306a36Sopenharmony_ci			 be64_to_cpu(crq->IU_data_ptr));
199362306a36Sopenharmony_ci	if (!rc) {
199462306a36Sopenharmony_ci		cmd->rsp.format = VIOSRP_MAD_FORMAT;
199562306a36Sopenharmony_ci		cmd->rsp.len = sizeof(struct mad_common);
199662306a36Sopenharmony_ci		cmd->rsp.tag = mad->tag;
199762306a36Sopenharmony_ci		list_add_tail(&cmd->list, &vscsi->waiting_rsp);
199862306a36Sopenharmony_ci		ibmvscsis_send_messages(vscsi);
199962306a36Sopenharmony_ci	} else {
200062306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "Error sending mad response, rc %ld\n",
200162306a36Sopenharmony_ci			rc);
200262306a36Sopenharmony_ci		if (rc == H_PERMISSION) {
200362306a36Sopenharmony_ci			if (connection_broken(vscsi))
200462306a36Sopenharmony_ci				flag_bits = (RESPONSE_Q_DOWN | CLIENT_FAILED);
200562306a36Sopenharmony_ci		}
200662306a36Sopenharmony_ci		dev_err(&vscsi->dev, "mad: failed to copy to client, rc %ld\n",
200762306a36Sopenharmony_ci			rc);
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci		ibmvscsis_free_cmd_resources(vscsi, cmd);
201062306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT,
201162306a36Sopenharmony_ci					  flag_bits);
201262306a36Sopenharmony_ci	}
201362306a36Sopenharmony_ci}
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci/**
201662306a36Sopenharmony_ci * ibmvscsis_mad() - Service a MAnagement Data gram.
201762306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
201862306a36Sopenharmony_ci * @crq:	Pointer to the CRQ entry containing the MAD request
201962306a36Sopenharmony_ci *
202062306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
202162306a36Sopenharmony_ci *	Interrupt, called with adapter lock held
202262306a36Sopenharmony_ci */
202362306a36Sopenharmony_cistatic long ibmvscsis_mad(struct scsi_info *vscsi, struct viosrp_crq *crq)
202462306a36Sopenharmony_ci{
202562306a36Sopenharmony_ci	struct iu_entry *iue;
202662306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd;
202762306a36Sopenharmony_ci	struct mad_common *mad;
202862306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci	switch (vscsi->state) {
203162306a36Sopenharmony_ci		/*
203262306a36Sopenharmony_ci		 * We have not exchanged Init Msgs yet, so this MAD was sent
203362306a36Sopenharmony_ci		 * before the last Transport Event; client will not be
203462306a36Sopenharmony_ci		 * expecting a response.
203562306a36Sopenharmony_ci		 */
203662306a36Sopenharmony_ci	case WAIT_CONNECTION:
203762306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "mad: in Wait Connection state, ignoring MAD, flags %d\n",
203862306a36Sopenharmony_ci			vscsi->flags);
203962306a36Sopenharmony_ci		return ADAPT_SUCCESS;
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	case SRP_PROCESSING:
204262306a36Sopenharmony_ci	case CONNECTED:
204362306a36Sopenharmony_ci		break;
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci		/*
204662306a36Sopenharmony_ci		 * We should never get here while we're in these states.
204762306a36Sopenharmony_ci		 * Just log an error and get out.
204862306a36Sopenharmony_ci		 */
204962306a36Sopenharmony_ci	case UNCONFIGURING:
205062306a36Sopenharmony_ci	case WAIT_IDLE:
205162306a36Sopenharmony_ci	case ERR_DISCONNECT:
205262306a36Sopenharmony_ci	case ERR_DISCONNECT_RECONNECT:
205362306a36Sopenharmony_ci	default:
205462306a36Sopenharmony_ci		dev_err(&vscsi->dev, "mad: invalid adapter state %d for mad\n",
205562306a36Sopenharmony_ci			vscsi->state);
205662306a36Sopenharmony_ci		return ADAPT_SUCCESS;
205762306a36Sopenharmony_ci	}
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	cmd = ibmvscsis_get_free_cmd(vscsi);
206062306a36Sopenharmony_ci	if (!cmd) {
206162306a36Sopenharmony_ci		dev_err(&vscsi->dev, "mad: failed to get cmd, debit %d\n",
206262306a36Sopenharmony_ci			vscsi->debit);
206362306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
206462306a36Sopenharmony_ci		return ERROR;
206562306a36Sopenharmony_ci	}
206662306a36Sopenharmony_ci	iue = cmd->iue;
206762306a36Sopenharmony_ci	cmd->type = ADAPTER_MAD;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	rc = ibmvscsis_copy_crq_packet(vscsi, cmd, crq);
207062306a36Sopenharmony_ci	if (!rc) {
207162306a36Sopenharmony_ci		mad = (struct mad_common *)&vio_iu(iue)->mad;
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "mad: type %d\n", be32_to_cpu(mad->type));
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci		rc = ibmvscsis_process_mad(vscsi, iue);
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "mad: status %hd, rc %ld\n",
207862306a36Sopenharmony_ci			be16_to_cpu(mad->status), rc);
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci		if (!rc)
208162306a36Sopenharmony_ci			ibmvscsis_send_mad_resp(vscsi, cmd, crq);
208262306a36Sopenharmony_ci	} else {
208362306a36Sopenharmony_ci		ibmvscsis_free_cmd_resources(vscsi, cmd);
208462306a36Sopenharmony_ci	}
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "Leaving mad, rc %ld\n", rc);
208762306a36Sopenharmony_ci	return rc;
208862306a36Sopenharmony_ci}
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci/**
209162306a36Sopenharmony_ci * ibmvscsis_login_rsp() - Create/copy a login response notice to the client
209262306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
209362306a36Sopenharmony_ci * @cmd:	Pointer to the command for the SRP Login request
209462306a36Sopenharmony_ci *
209562306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
209662306a36Sopenharmony_ci *	Interrupt, interrupt lock held
209762306a36Sopenharmony_ci */
209862306a36Sopenharmony_cistatic long ibmvscsis_login_rsp(struct scsi_info *vscsi,
209962306a36Sopenharmony_ci				struct ibmvscsis_cmd *cmd)
210062306a36Sopenharmony_ci{
210162306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
210262306a36Sopenharmony_ci	struct srp_login_rsp *rsp = &vio_iu(iue)->srp.login_rsp;
210362306a36Sopenharmony_ci	struct format_code *fmt;
210462306a36Sopenharmony_ci	uint flag_bits = 0;
210562306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	memset(rsp, 0, sizeof(struct srp_login_rsp));
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	rsp->opcode = SRP_LOGIN_RSP;
211062306a36Sopenharmony_ci	rsp->req_lim_delta = cpu_to_be32(vscsi->request_limit);
211162306a36Sopenharmony_ci	rsp->tag = cmd->rsp.tag;
211262306a36Sopenharmony_ci	rsp->max_it_iu_len = cpu_to_be32(SRP_MAX_IU_LEN);
211362306a36Sopenharmony_ci	rsp->max_ti_iu_len = cpu_to_be32(SRP_MAX_IU_LEN);
211462306a36Sopenharmony_ci	fmt = (struct format_code *)&rsp->buf_fmt;
211562306a36Sopenharmony_ci	fmt->buffers = SUPPORTED_FORMATS;
211662306a36Sopenharmony_ci	vscsi->credit = 0;
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	cmd->rsp.len = sizeof(struct srp_login_rsp);
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	dma_wmb();
212162306a36Sopenharmony_ci	rc = h_copy_rdma(cmd->rsp.len, vscsi->dds.window[LOCAL].liobn,
212262306a36Sopenharmony_ci			 iue->sbuf->dma, vscsi->dds.window[REMOTE].liobn,
212362306a36Sopenharmony_ci			 be64_to_cpu(iue->remote_token));
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	switch (rc) {
212662306a36Sopenharmony_ci	case H_SUCCESS:
212762306a36Sopenharmony_ci		break;
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	case H_PERMISSION:
213062306a36Sopenharmony_ci		if (connection_broken(vscsi))
213162306a36Sopenharmony_ci			flag_bits = RESPONSE_Q_DOWN | CLIENT_FAILED;
213262306a36Sopenharmony_ci		dev_err(&vscsi->dev, "login_rsp: error copying to client, rc %ld\n",
213362306a36Sopenharmony_ci			rc);
213462306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT,
213562306a36Sopenharmony_ci					  flag_bits);
213662306a36Sopenharmony_ci		break;
213762306a36Sopenharmony_ci	case H_SOURCE_PARM:
213862306a36Sopenharmony_ci	case H_DEST_PARM:
213962306a36Sopenharmony_ci	default:
214062306a36Sopenharmony_ci		dev_err(&vscsi->dev, "login_rsp: error copying to client, rc %ld\n",
214162306a36Sopenharmony_ci			rc);
214262306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
214362306a36Sopenharmony_ci		break;
214462306a36Sopenharmony_ci	}
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	return rc;
214762306a36Sopenharmony_ci}
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci/**
215062306a36Sopenharmony_ci * ibmvscsis_srp_login_rej() - Create/copy a login rejection notice to client
215162306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
215262306a36Sopenharmony_ci * @cmd:	Pointer to the command for the SRP Login request
215362306a36Sopenharmony_ci * @reason:	The reason the SRP Login is being rejected, per SRP protocol
215462306a36Sopenharmony_ci *
215562306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
215662306a36Sopenharmony_ci *	Interrupt, interrupt lock held
215762306a36Sopenharmony_ci */
215862306a36Sopenharmony_cistatic long ibmvscsis_srp_login_rej(struct scsi_info *vscsi,
215962306a36Sopenharmony_ci				    struct ibmvscsis_cmd *cmd, u32 reason)
216062306a36Sopenharmony_ci{
216162306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
216262306a36Sopenharmony_ci	struct srp_login_rej *rej = &vio_iu(iue)->srp.login_rej;
216362306a36Sopenharmony_ci	struct format_code *fmt;
216462306a36Sopenharmony_ci	uint flag_bits = 0;
216562306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	memset(rej, 0, sizeof(*rej));
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	rej->opcode = SRP_LOGIN_REJ;
217062306a36Sopenharmony_ci	rej->reason = cpu_to_be32(reason);
217162306a36Sopenharmony_ci	rej->tag = cmd->rsp.tag;
217262306a36Sopenharmony_ci	fmt = (struct format_code *)&rej->buf_fmt;
217362306a36Sopenharmony_ci	fmt->buffers = SUPPORTED_FORMATS;
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	cmd->rsp.len = sizeof(*rej);
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	dma_wmb();
217862306a36Sopenharmony_ci	rc = h_copy_rdma(cmd->rsp.len, vscsi->dds.window[LOCAL].liobn,
217962306a36Sopenharmony_ci			 iue->sbuf->dma, vscsi->dds.window[REMOTE].liobn,
218062306a36Sopenharmony_ci			 be64_to_cpu(iue->remote_token));
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	switch (rc) {
218362306a36Sopenharmony_ci	case H_SUCCESS:
218462306a36Sopenharmony_ci		break;
218562306a36Sopenharmony_ci	case H_PERMISSION:
218662306a36Sopenharmony_ci		if (connection_broken(vscsi))
218762306a36Sopenharmony_ci			flag_bits = RESPONSE_Q_DOWN | CLIENT_FAILED;
218862306a36Sopenharmony_ci		dev_err(&vscsi->dev, "login_rej: error copying to client, rc %ld\n",
218962306a36Sopenharmony_ci			rc);
219062306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT,
219162306a36Sopenharmony_ci					  flag_bits);
219262306a36Sopenharmony_ci		break;
219362306a36Sopenharmony_ci	case H_SOURCE_PARM:
219462306a36Sopenharmony_ci	case H_DEST_PARM:
219562306a36Sopenharmony_ci	default:
219662306a36Sopenharmony_ci		dev_err(&vscsi->dev, "login_rej: error copying to client, rc %ld\n",
219762306a36Sopenharmony_ci			rc);
219862306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
219962306a36Sopenharmony_ci		break;
220062306a36Sopenharmony_ci	}
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	return rc;
220362306a36Sopenharmony_ci}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_cistatic int ibmvscsis_make_nexus(struct ibmvscsis_tport *tport)
220662306a36Sopenharmony_ci{
220762306a36Sopenharmony_ci	char *name = tport->tport_name;
220862306a36Sopenharmony_ci	struct ibmvscsis_nexus *nexus;
220962306a36Sopenharmony_ci	struct scsi_info *vscsi = container_of(tport, struct scsi_info, tport);
221062306a36Sopenharmony_ci	int rc;
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci	if (tport->ibmv_nexus) {
221362306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "tport->ibmv_nexus already exists\n");
221462306a36Sopenharmony_ci		return 0;
221562306a36Sopenharmony_ci	}
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	nexus = kzalloc(sizeof(*nexus), GFP_KERNEL);
221862306a36Sopenharmony_ci	if (!nexus) {
221962306a36Sopenharmony_ci		dev_err(&vscsi->dev, "Unable to allocate struct ibmvscsis_nexus\n");
222062306a36Sopenharmony_ci		return -ENOMEM;
222162306a36Sopenharmony_ci	}
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	nexus->se_sess = target_setup_session(&tport->se_tpg, 0, 0,
222462306a36Sopenharmony_ci					      TARGET_PROT_NORMAL, name, nexus,
222562306a36Sopenharmony_ci					      NULL);
222662306a36Sopenharmony_ci	if (IS_ERR(nexus->se_sess)) {
222762306a36Sopenharmony_ci		rc = PTR_ERR(nexus->se_sess);
222862306a36Sopenharmony_ci		goto transport_init_fail;
222962306a36Sopenharmony_ci	}
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci	tport->ibmv_nexus = nexus;
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci	return 0;
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_citransport_init_fail:
223662306a36Sopenharmony_ci	kfree(nexus);
223762306a36Sopenharmony_ci	return rc;
223862306a36Sopenharmony_ci}
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_cistatic int ibmvscsis_drop_nexus(struct ibmvscsis_tport *tport)
224162306a36Sopenharmony_ci{
224262306a36Sopenharmony_ci	struct se_session *se_sess;
224362306a36Sopenharmony_ci	struct ibmvscsis_nexus *nexus;
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	nexus = tport->ibmv_nexus;
224662306a36Sopenharmony_ci	if (!nexus)
224762306a36Sopenharmony_ci		return -ENODEV;
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	se_sess = nexus->se_sess;
225062306a36Sopenharmony_ci	if (!se_sess)
225162306a36Sopenharmony_ci		return -ENODEV;
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	/*
225462306a36Sopenharmony_ci	 * Release the SCSI I_T Nexus to the emulated ibmvscsis Target Port
225562306a36Sopenharmony_ci	 */
225662306a36Sopenharmony_ci	target_remove_session(se_sess);
225762306a36Sopenharmony_ci	tport->ibmv_nexus = NULL;
225862306a36Sopenharmony_ci	kfree(nexus);
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	return 0;
226162306a36Sopenharmony_ci}
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci/**
226462306a36Sopenharmony_ci * ibmvscsis_srp_login() - Process an SRP Login Request
226562306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
226662306a36Sopenharmony_ci * @cmd:	Command element to use to process the SRP Login request
226762306a36Sopenharmony_ci * @crq:	Pointer to CRQ entry containing the SRP Login request
226862306a36Sopenharmony_ci *
226962306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
227062306a36Sopenharmony_ci *	Interrupt, called with interrupt lock held
227162306a36Sopenharmony_ci */
227262306a36Sopenharmony_cistatic long ibmvscsis_srp_login(struct scsi_info *vscsi,
227362306a36Sopenharmony_ci				struct ibmvscsis_cmd *cmd,
227462306a36Sopenharmony_ci				struct viosrp_crq *crq)
227562306a36Sopenharmony_ci{
227662306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
227762306a36Sopenharmony_ci	struct srp_login_req *req = &vio_iu(iue)->srp.login_req;
227862306a36Sopenharmony_ci	struct port_id {
227962306a36Sopenharmony_ci		__be64 id_extension;
228062306a36Sopenharmony_ci		__be64 io_guid;
228162306a36Sopenharmony_ci	} *iport, *tport;
228262306a36Sopenharmony_ci	struct format_code *fmt;
228362306a36Sopenharmony_ci	u32 reason = 0x0;
228462306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	iport = (struct port_id *)req->initiator_port_id;
228762306a36Sopenharmony_ci	tport = (struct port_id *)req->target_port_id;
228862306a36Sopenharmony_ci	fmt = (struct format_code *)&req->req_buf_fmt;
228962306a36Sopenharmony_ci	if (be32_to_cpu(req->req_it_iu_len) > SRP_MAX_IU_LEN)
229062306a36Sopenharmony_ci		reason = SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE;
229162306a36Sopenharmony_ci	else if (be32_to_cpu(req->req_it_iu_len) < 64)
229262306a36Sopenharmony_ci		reason = SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL;
229362306a36Sopenharmony_ci	else if ((be64_to_cpu(iport->id_extension) > (MAX_NUM_PORTS - 1)) ||
229462306a36Sopenharmony_ci		 (be64_to_cpu(tport->id_extension) > (MAX_NUM_PORTS - 1)))
229562306a36Sopenharmony_ci		reason = SRP_LOGIN_REJ_UNABLE_ASSOCIATE_CHANNEL;
229662306a36Sopenharmony_ci	else if (req->req_flags & SRP_MULTICHAN_MULTI)
229762306a36Sopenharmony_ci		reason = SRP_LOGIN_REJ_MULTI_CHANNEL_UNSUPPORTED;
229862306a36Sopenharmony_ci	else if (fmt->buffers & (~SUPPORTED_FORMATS))
229962306a36Sopenharmony_ci		reason = SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT;
230062306a36Sopenharmony_ci	else if ((fmt->buffers & SUPPORTED_FORMATS) == 0)
230162306a36Sopenharmony_ci		reason = SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT;
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	if (vscsi->state == SRP_PROCESSING)
230462306a36Sopenharmony_ci		reason = SRP_LOGIN_REJ_CHANNEL_LIMIT_REACHED;
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_ci	rc = ibmvscsis_make_nexus(&vscsi->tport);
230762306a36Sopenharmony_ci	if (rc)
230862306a36Sopenharmony_ci		reason = SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL;
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci	cmd->rsp.format = VIOSRP_SRP_FORMAT;
231162306a36Sopenharmony_ci	cmd->rsp.tag = req->tag;
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "srp_login: reason 0x%x\n", reason);
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci	if (reason)
231662306a36Sopenharmony_ci		rc = ibmvscsis_srp_login_rej(vscsi, cmd, reason);
231762306a36Sopenharmony_ci	else
231862306a36Sopenharmony_ci		rc = ibmvscsis_login_rsp(vscsi, cmd);
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	if (!rc) {
232162306a36Sopenharmony_ci		if (!reason)
232262306a36Sopenharmony_ci			vscsi->state = SRP_PROCESSING;
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci		list_add_tail(&cmd->list, &vscsi->waiting_rsp);
232562306a36Sopenharmony_ci		ibmvscsis_send_messages(vscsi);
232662306a36Sopenharmony_ci	} else {
232762306a36Sopenharmony_ci		ibmvscsis_free_cmd_resources(vscsi, cmd);
232862306a36Sopenharmony_ci	}
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "Leaving srp_login, rc %ld\n", rc);
233162306a36Sopenharmony_ci	return rc;
233262306a36Sopenharmony_ci}
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci/**
233562306a36Sopenharmony_ci * ibmvscsis_srp_i_logout() - Helper Function to close I_T Nexus
233662306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
233762306a36Sopenharmony_ci * @cmd:	Command element to use to process the Implicit Logout request
233862306a36Sopenharmony_ci * @crq:	Pointer to CRQ entry containing the Implicit Logout request
233962306a36Sopenharmony_ci *
234062306a36Sopenharmony_ci * Do the logic to close the I_T nexus.  This function may not
234162306a36Sopenharmony_ci * behave to specification.
234262306a36Sopenharmony_ci *
234362306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
234462306a36Sopenharmony_ci *	Interrupt, interrupt lock held
234562306a36Sopenharmony_ci */
234662306a36Sopenharmony_cistatic long ibmvscsis_srp_i_logout(struct scsi_info *vscsi,
234762306a36Sopenharmony_ci				   struct ibmvscsis_cmd *cmd,
234862306a36Sopenharmony_ci				   struct viosrp_crq *crq)
234962306a36Sopenharmony_ci{
235062306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
235162306a36Sopenharmony_ci	struct srp_i_logout *log_out = &vio_iu(iue)->srp.i_logout;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	if ((vscsi->debit > 0) || !list_empty(&vscsi->schedule_q) ||
235462306a36Sopenharmony_ci	    !list_empty(&vscsi->waiting_rsp)) {
235562306a36Sopenharmony_ci		dev_err(&vscsi->dev, "i_logout: outstanding work\n");
235662306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0);
235762306a36Sopenharmony_ci	} else {
235862306a36Sopenharmony_ci		cmd->rsp.format = SRP_FORMAT;
235962306a36Sopenharmony_ci		cmd->rsp.tag = log_out->tag;
236062306a36Sopenharmony_ci		cmd->rsp.len = sizeof(struct mad_common);
236162306a36Sopenharmony_ci		list_add_tail(&cmd->list, &vscsi->waiting_rsp);
236262306a36Sopenharmony_ci		ibmvscsis_send_messages(vscsi);
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 0);
236562306a36Sopenharmony_ci	}
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_ci	return ADAPT_SUCCESS;
236862306a36Sopenharmony_ci}
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci/* Called with intr lock held */
237162306a36Sopenharmony_cistatic void ibmvscsis_srp_cmd(struct scsi_info *vscsi, struct viosrp_crq *crq)
237262306a36Sopenharmony_ci{
237362306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd;
237462306a36Sopenharmony_ci	struct iu_entry *iue;
237562306a36Sopenharmony_ci	struct srp_cmd *srp;
237662306a36Sopenharmony_ci	struct srp_tsk_mgmt *tsk;
237762306a36Sopenharmony_ci	long rc;
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	if (vscsi->request_limit - vscsi->debit <= 0) {
238062306a36Sopenharmony_ci		/* Client has exceeded request limit */
238162306a36Sopenharmony_ci		dev_err(&vscsi->dev, "Client exceeded the request limit (%d), debit %d\n",
238262306a36Sopenharmony_ci			vscsi->request_limit, vscsi->debit);
238362306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
238462306a36Sopenharmony_ci		return;
238562306a36Sopenharmony_ci	}
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci	cmd = ibmvscsis_get_free_cmd(vscsi);
238862306a36Sopenharmony_ci	if (!cmd) {
238962306a36Sopenharmony_ci		dev_err(&vscsi->dev, "srp_cmd failed to get cmd, debit %d\n",
239062306a36Sopenharmony_ci			vscsi->debit);
239162306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
239262306a36Sopenharmony_ci		return;
239362306a36Sopenharmony_ci	}
239462306a36Sopenharmony_ci	iue = cmd->iue;
239562306a36Sopenharmony_ci	srp = &vio_iu(iue)->srp.cmd;
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	rc = ibmvscsis_copy_crq_packet(vscsi, cmd, crq);
239862306a36Sopenharmony_ci	if (rc) {
239962306a36Sopenharmony_ci		ibmvscsis_free_cmd_resources(vscsi, cmd);
240062306a36Sopenharmony_ci		return;
240162306a36Sopenharmony_ci	}
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	if (vscsi->state == SRP_PROCESSING) {
240462306a36Sopenharmony_ci		switch (srp->opcode) {
240562306a36Sopenharmony_ci		case SRP_LOGIN_REQ:
240662306a36Sopenharmony_ci			rc = ibmvscsis_srp_login(vscsi, cmd, crq);
240762306a36Sopenharmony_ci			break;
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci		case SRP_TSK_MGMT:
241062306a36Sopenharmony_ci			tsk = &vio_iu(iue)->srp.tsk_mgmt;
241162306a36Sopenharmony_ci			dev_dbg(&vscsi->dev, "tsk_mgmt tag: %llu (0x%llx)\n",
241262306a36Sopenharmony_ci				tsk->tag, tsk->tag);
241362306a36Sopenharmony_ci			cmd->rsp.tag = tsk->tag;
241462306a36Sopenharmony_ci			vscsi->debit += 1;
241562306a36Sopenharmony_ci			cmd->type = TASK_MANAGEMENT;
241662306a36Sopenharmony_ci			list_add_tail(&cmd->list, &vscsi->schedule_q);
241762306a36Sopenharmony_ci			queue_work(vscsi->work_q, &cmd->work);
241862306a36Sopenharmony_ci			break;
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci		case SRP_CMD:
242162306a36Sopenharmony_ci			dev_dbg(&vscsi->dev, "srp_cmd tag: %llu (0x%llx)\n",
242262306a36Sopenharmony_ci				srp->tag, srp->tag);
242362306a36Sopenharmony_ci			cmd->rsp.tag = srp->tag;
242462306a36Sopenharmony_ci			vscsi->debit += 1;
242562306a36Sopenharmony_ci			cmd->type = SCSI_CDB;
242662306a36Sopenharmony_ci			/*
242762306a36Sopenharmony_ci			 * We want to keep track of work waiting for
242862306a36Sopenharmony_ci			 * the workqueue.
242962306a36Sopenharmony_ci			 */
243062306a36Sopenharmony_ci			list_add_tail(&cmd->list, &vscsi->schedule_q);
243162306a36Sopenharmony_ci			queue_work(vscsi->work_q, &cmd->work);
243262306a36Sopenharmony_ci			break;
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci		case SRP_I_LOGOUT:
243562306a36Sopenharmony_ci			rc = ibmvscsis_srp_i_logout(vscsi, cmd, crq);
243662306a36Sopenharmony_ci			break;
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci		case SRP_CRED_RSP:
243962306a36Sopenharmony_ci		case SRP_AER_RSP:
244062306a36Sopenharmony_ci		default:
244162306a36Sopenharmony_ci			ibmvscsis_free_cmd_resources(vscsi, cmd);
244262306a36Sopenharmony_ci			dev_err(&vscsi->dev, "invalid srp cmd, opcode %d\n",
244362306a36Sopenharmony_ci				(uint)srp->opcode);
244462306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi,
244562306a36Sopenharmony_ci						  ERR_DISCONNECT_RECONNECT, 0);
244662306a36Sopenharmony_ci			break;
244762306a36Sopenharmony_ci		}
244862306a36Sopenharmony_ci	} else if (srp->opcode == SRP_LOGIN_REQ && vscsi->state == CONNECTED) {
244962306a36Sopenharmony_ci		rc = ibmvscsis_srp_login(vscsi, cmd, crq);
245062306a36Sopenharmony_ci	} else {
245162306a36Sopenharmony_ci		ibmvscsis_free_cmd_resources(vscsi, cmd);
245262306a36Sopenharmony_ci		dev_err(&vscsi->dev, "Invalid state %d to handle srp cmd\n",
245362306a36Sopenharmony_ci			vscsi->state);
245462306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
245562306a36Sopenharmony_ci	}
245662306a36Sopenharmony_ci}
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci/**
245962306a36Sopenharmony_ci * ibmvscsis_ping_response() - Respond to a ping request
246062306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
246162306a36Sopenharmony_ci *
246262306a36Sopenharmony_ci * Let the client know that the server is alive and waiting on
246362306a36Sopenharmony_ci * its native I/O stack.
246462306a36Sopenharmony_ci * If any type of error occurs from the call to queue a ping
246562306a36Sopenharmony_ci * response then the client is either not accepting or receiving
246662306a36Sopenharmony_ci * interrupts.  Disconnect with an error.
246762306a36Sopenharmony_ci *
246862306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
246962306a36Sopenharmony_ci *	Interrupt, interrupt lock held
247062306a36Sopenharmony_ci */
247162306a36Sopenharmony_cistatic long ibmvscsis_ping_response(struct scsi_info *vscsi)
247262306a36Sopenharmony_ci{
247362306a36Sopenharmony_ci	struct viosrp_crq *crq;
247462306a36Sopenharmony_ci	u64 buffer[2] = { 0, 0 };
247562306a36Sopenharmony_ci	long rc;
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci	crq = (struct viosrp_crq *)&buffer;
247862306a36Sopenharmony_ci	crq->valid = VALID_CMD_RESP_EL;
247962306a36Sopenharmony_ci	crq->format = (u8)MESSAGE_IN_CRQ;
248062306a36Sopenharmony_ci	crq->status = PING_RESPONSE;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	rc = h_send_crq(vscsi->dds.unit_id, cpu_to_be64(buffer[MSG_HI]),
248362306a36Sopenharmony_ci			cpu_to_be64(buffer[MSG_LOW]));
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	switch (rc) {
248662306a36Sopenharmony_ci	case H_SUCCESS:
248762306a36Sopenharmony_ci		break;
248862306a36Sopenharmony_ci	case H_CLOSED:
248962306a36Sopenharmony_ci		vscsi->flags |= CLIENT_FAILED;
249062306a36Sopenharmony_ci		fallthrough;
249162306a36Sopenharmony_ci	case H_DROPPED:
249262306a36Sopenharmony_ci		vscsi->flags |= RESPONSE_Q_DOWN;
249362306a36Sopenharmony_ci		fallthrough;
249462306a36Sopenharmony_ci	case H_REMOTE_PARM:
249562306a36Sopenharmony_ci		dev_err(&vscsi->dev, "ping_response: h_send_crq failed, rc %ld\n",
249662306a36Sopenharmony_ci			rc);
249762306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
249862306a36Sopenharmony_ci		break;
249962306a36Sopenharmony_ci	default:
250062306a36Sopenharmony_ci		dev_err(&vscsi->dev, "ping_response: h_send_crq returned unknown rc %ld\n",
250162306a36Sopenharmony_ci			rc);
250262306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0);
250362306a36Sopenharmony_ci		break;
250462306a36Sopenharmony_ci	}
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	return rc;
250762306a36Sopenharmony_ci}
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_ci/**
251062306a36Sopenharmony_ci * ibmvscsis_parse_command() - Parse an element taken from the cmd rsp queue.
251162306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
251262306a36Sopenharmony_ci * @crq:	Pointer to CRQ element containing the SRP request
251362306a36Sopenharmony_ci *
251462306a36Sopenharmony_ci * This function will return success if the command queue element is valid
251562306a36Sopenharmony_ci * and the srp iu or MAD request it pointed to was also valid.  That does
251662306a36Sopenharmony_ci * not mean that an error was not returned to the client.
251762306a36Sopenharmony_ci *
251862306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
251962306a36Sopenharmony_ci *	Interrupt, intr lock held
252062306a36Sopenharmony_ci */
252162306a36Sopenharmony_cistatic long ibmvscsis_parse_command(struct scsi_info *vscsi,
252262306a36Sopenharmony_ci				    struct viosrp_crq *crq)
252362306a36Sopenharmony_ci{
252462306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	switch (crq->valid) {
252762306a36Sopenharmony_ci	case VALID_CMD_RESP_EL:
252862306a36Sopenharmony_ci		switch (crq->format) {
252962306a36Sopenharmony_ci		case OS400_FORMAT:
253062306a36Sopenharmony_ci		case AIX_FORMAT:
253162306a36Sopenharmony_ci		case LINUX_FORMAT:
253262306a36Sopenharmony_ci		case MAD_FORMAT:
253362306a36Sopenharmony_ci			if (vscsi->flags & PROCESSING_MAD) {
253462306a36Sopenharmony_ci				rc = ERROR;
253562306a36Sopenharmony_ci				dev_err(&vscsi->dev, "parse_command: already processing mad\n");
253662306a36Sopenharmony_ci				ibmvscsis_post_disconnect(vscsi,
253762306a36Sopenharmony_ci						       ERR_DISCONNECT_RECONNECT,
253862306a36Sopenharmony_ci						       0);
253962306a36Sopenharmony_ci			} else {
254062306a36Sopenharmony_ci				vscsi->flags |= PROCESSING_MAD;
254162306a36Sopenharmony_ci				rc = ibmvscsis_mad(vscsi, crq);
254262306a36Sopenharmony_ci			}
254362306a36Sopenharmony_ci			break;
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci		case SRP_FORMAT:
254662306a36Sopenharmony_ci			ibmvscsis_srp_cmd(vscsi, crq);
254762306a36Sopenharmony_ci			break;
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_ci		case MESSAGE_IN_CRQ:
255062306a36Sopenharmony_ci			if (crq->status == PING)
255162306a36Sopenharmony_ci				ibmvscsis_ping_response(vscsi);
255262306a36Sopenharmony_ci			break;
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci		default:
255562306a36Sopenharmony_ci			dev_err(&vscsi->dev, "parse_command: invalid format %d\n",
255662306a36Sopenharmony_ci				(uint)crq->format);
255762306a36Sopenharmony_ci			ibmvscsis_post_disconnect(vscsi,
255862306a36Sopenharmony_ci						  ERR_DISCONNECT_RECONNECT, 0);
255962306a36Sopenharmony_ci			break;
256062306a36Sopenharmony_ci		}
256162306a36Sopenharmony_ci		break;
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci	case VALID_TRANS_EVENT:
256462306a36Sopenharmony_ci		rc = ibmvscsis_trans_event(vscsi, crq);
256562306a36Sopenharmony_ci		break;
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ci	case VALID_INIT_MSG:
256862306a36Sopenharmony_ci		rc = ibmvscsis_init_msg(vscsi, crq);
256962306a36Sopenharmony_ci		break;
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	default:
257262306a36Sopenharmony_ci		dev_err(&vscsi->dev, "parse_command: invalid valid field %d\n",
257362306a36Sopenharmony_ci			(uint)crq->valid);
257462306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
257562306a36Sopenharmony_ci		break;
257662306a36Sopenharmony_ci	}
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	/*
257962306a36Sopenharmony_ci	 * Return only what the interrupt handler cares
258062306a36Sopenharmony_ci	 * about. Most errors we keep right on trucking.
258162306a36Sopenharmony_ci	 */
258262306a36Sopenharmony_ci	rc = vscsi->flags & SCHEDULE_DISCONNECT;
258362306a36Sopenharmony_ci
258462306a36Sopenharmony_ci	return rc;
258562306a36Sopenharmony_ci}
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_cistatic int read_dma_window(struct scsi_info *vscsi)
258862306a36Sopenharmony_ci{
258962306a36Sopenharmony_ci	struct vio_dev *vdev = vscsi->dma_dev;
259062306a36Sopenharmony_ci	const __be32 *dma_window;
259162306a36Sopenharmony_ci	const __be32 *prop;
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ci	/* TODO Using of_parse_dma_window would be better, but it doesn't give
259462306a36Sopenharmony_ci	 * a way to read multiple windows without already knowing the size of
259562306a36Sopenharmony_ci	 * a window or the number of windows.
259662306a36Sopenharmony_ci	 */
259762306a36Sopenharmony_ci	dma_window = (const __be32 *)vio_get_attribute(vdev,
259862306a36Sopenharmony_ci						       "ibm,my-dma-window",
259962306a36Sopenharmony_ci						       NULL);
260062306a36Sopenharmony_ci	if (!dma_window) {
260162306a36Sopenharmony_ci		dev_err(&vscsi->dev, "Couldn't find ibm,my-dma-window property\n");
260262306a36Sopenharmony_ci		return -1;
260362306a36Sopenharmony_ci	}
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ci	vscsi->dds.window[LOCAL].liobn = be32_to_cpu(*dma_window);
260662306a36Sopenharmony_ci	dma_window++;
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-address-cells",
260962306a36Sopenharmony_ci						 NULL);
261062306a36Sopenharmony_ci	if (!prop) {
261162306a36Sopenharmony_ci		dev_warn(&vscsi->dev, "Couldn't find ibm,#dma-address-cells property\n");
261262306a36Sopenharmony_ci		dma_window++;
261362306a36Sopenharmony_ci	} else {
261462306a36Sopenharmony_ci		dma_window += be32_to_cpu(*prop);
261562306a36Sopenharmony_ci	}
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-size-cells",
261862306a36Sopenharmony_ci						 NULL);
261962306a36Sopenharmony_ci	if (!prop) {
262062306a36Sopenharmony_ci		dev_warn(&vscsi->dev, "Couldn't find ibm,#dma-size-cells property\n");
262162306a36Sopenharmony_ci		dma_window++;
262262306a36Sopenharmony_ci	} else {
262362306a36Sopenharmony_ci		dma_window += be32_to_cpu(*prop);
262462306a36Sopenharmony_ci	}
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_ci	/* dma_window should point to the second window now */
262762306a36Sopenharmony_ci	vscsi->dds.window[REMOTE].liobn = be32_to_cpu(*dma_window);
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci	return 0;
263062306a36Sopenharmony_ci}
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_cistatic struct ibmvscsis_tport *ibmvscsis_lookup_port(const char *name)
263362306a36Sopenharmony_ci{
263462306a36Sopenharmony_ci	struct ibmvscsis_tport *tport = NULL;
263562306a36Sopenharmony_ci	struct vio_dev *vdev;
263662306a36Sopenharmony_ci	struct scsi_info *vscsi;
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci	spin_lock_bh(&ibmvscsis_dev_lock);
263962306a36Sopenharmony_ci	list_for_each_entry(vscsi, &ibmvscsis_dev_list, list) {
264062306a36Sopenharmony_ci		vdev = vscsi->dma_dev;
264162306a36Sopenharmony_ci		if (!strcmp(dev_name(&vdev->dev), name)) {
264262306a36Sopenharmony_ci			tport = &vscsi->tport;
264362306a36Sopenharmony_ci			break;
264462306a36Sopenharmony_ci		}
264562306a36Sopenharmony_ci	}
264662306a36Sopenharmony_ci	spin_unlock_bh(&ibmvscsis_dev_lock);
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci	return tport;
264962306a36Sopenharmony_ci}
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci/**
265262306a36Sopenharmony_ci * ibmvscsis_parse_cmd() - Parse SRP Command
265362306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
265462306a36Sopenharmony_ci * @cmd:	Pointer to command element with SRP command
265562306a36Sopenharmony_ci *
265662306a36Sopenharmony_ci * Parse the srp command; if it is valid then submit it to tcm.
265762306a36Sopenharmony_ci * Note: The return code does not reflect the status of the SCSI CDB.
265862306a36Sopenharmony_ci *
265962306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
266062306a36Sopenharmony_ci *	Process level
266162306a36Sopenharmony_ci */
266262306a36Sopenharmony_cistatic void ibmvscsis_parse_cmd(struct scsi_info *vscsi,
266362306a36Sopenharmony_ci				struct ibmvscsis_cmd *cmd)
266462306a36Sopenharmony_ci{
266562306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
266662306a36Sopenharmony_ci	struct srp_cmd *srp = (struct srp_cmd *)iue->sbuf->buf;
266762306a36Sopenharmony_ci	struct ibmvscsis_nexus *nexus;
266862306a36Sopenharmony_ci	u64 data_len = 0;
266962306a36Sopenharmony_ci	enum dma_data_direction dir;
267062306a36Sopenharmony_ci	int attr = 0;
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci	nexus = vscsi->tport.ibmv_nexus;
267362306a36Sopenharmony_ci	/*
267462306a36Sopenharmony_ci	 * additional length in bytes.  Note that the SRP spec says that
267562306a36Sopenharmony_ci	 * additional length is in 4-byte words, but technically the
267662306a36Sopenharmony_ci	 * additional length field is only the upper 6 bits of the byte.
267762306a36Sopenharmony_ci	 * The lower 2 bits are reserved.  If the lower 2 bits are 0 (as
267862306a36Sopenharmony_ci	 * all reserved fields should be), then interpreting the byte as
267962306a36Sopenharmony_ci	 * an int will yield the length in bytes.
268062306a36Sopenharmony_ci	 */
268162306a36Sopenharmony_ci	if (srp->add_cdb_len & 0x03) {
268262306a36Sopenharmony_ci		dev_err(&vscsi->dev, "parse_cmd: reserved bits set in IU\n");
268362306a36Sopenharmony_ci		spin_lock_bh(&vscsi->intr_lock);
268462306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
268562306a36Sopenharmony_ci		ibmvscsis_free_cmd_resources(vscsi, cmd);
268662306a36Sopenharmony_ci		spin_unlock_bh(&vscsi->intr_lock);
268762306a36Sopenharmony_ci		return;
268862306a36Sopenharmony_ci	}
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci	if (srp_get_desc_table(srp, &dir, &data_len)) {
269162306a36Sopenharmony_ci		dev_err(&vscsi->dev, "0x%llx: parsing SRP descriptor table failed.\n",
269262306a36Sopenharmony_ci			srp->tag);
269362306a36Sopenharmony_ci		goto fail;
269462306a36Sopenharmony_ci	}
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	cmd->rsp.sol_not = srp->sol_not;
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci	switch (srp->task_attr) {
269962306a36Sopenharmony_ci	case SRP_SIMPLE_TASK:
270062306a36Sopenharmony_ci		attr = TCM_SIMPLE_TAG;
270162306a36Sopenharmony_ci		break;
270262306a36Sopenharmony_ci	case SRP_ORDERED_TASK:
270362306a36Sopenharmony_ci		attr = TCM_ORDERED_TAG;
270462306a36Sopenharmony_ci		break;
270562306a36Sopenharmony_ci	case SRP_HEAD_TASK:
270662306a36Sopenharmony_ci		attr = TCM_HEAD_TAG;
270762306a36Sopenharmony_ci		break;
270862306a36Sopenharmony_ci	case SRP_ACA_TASK:
270962306a36Sopenharmony_ci		attr = TCM_ACA_TAG;
271062306a36Sopenharmony_ci		break;
271162306a36Sopenharmony_ci	default:
271262306a36Sopenharmony_ci		dev_err(&vscsi->dev, "Invalid task attribute %d\n",
271362306a36Sopenharmony_ci			srp->task_attr);
271462306a36Sopenharmony_ci		goto fail;
271562306a36Sopenharmony_ci	}
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_ci	cmd->se_cmd.tag = be64_to_cpu(srp->tag);
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci	spin_lock_bh(&vscsi->intr_lock);
272062306a36Sopenharmony_ci	list_add_tail(&cmd->list, &vscsi->active_q);
272162306a36Sopenharmony_ci	spin_unlock_bh(&vscsi->intr_lock);
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci	srp->lun.scsi_lun[0] &= 0x3f;
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_ci	target_submit_cmd(&cmd->se_cmd, nexus->se_sess, srp->cdb,
272662306a36Sopenharmony_ci			  cmd->sense_buf, scsilun_to_int(&srp->lun),
272762306a36Sopenharmony_ci			  data_len, attr, dir, 0);
272862306a36Sopenharmony_ci	return;
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_cifail:
273162306a36Sopenharmony_ci	spin_lock_bh(&vscsi->intr_lock);
273262306a36Sopenharmony_ci	ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0);
273362306a36Sopenharmony_ci	spin_unlock_bh(&vscsi->intr_lock);
273462306a36Sopenharmony_ci}
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci/**
273762306a36Sopenharmony_ci * ibmvscsis_parse_task() - Parse SRP Task Management Request
273862306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
273962306a36Sopenharmony_ci * @cmd:	Pointer to command element with SRP task management request
274062306a36Sopenharmony_ci *
274162306a36Sopenharmony_ci * Parse the srp task management request; if it is valid then submit it to tcm.
274262306a36Sopenharmony_ci * Note: The return code does not reflect the status of the task management
274362306a36Sopenharmony_ci * request.
274462306a36Sopenharmony_ci *
274562306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
274662306a36Sopenharmony_ci *	Processor level
274762306a36Sopenharmony_ci */
274862306a36Sopenharmony_cistatic void ibmvscsis_parse_task(struct scsi_info *vscsi,
274962306a36Sopenharmony_ci				 struct ibmvscsis_cmd *cmd)
275062306a36Sopenharmony_ci{
275162306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
275262306a36Sopenharmony_ci	struct srp_tsk_mgmt *srp_tsk = &vio_iu(iue)->srp.tsk_mgmt;
275362306a36Sopenharmony_ci	int tcm_type;
275462306a36Sopenharmony_ci	u64 tag_to_abort = 0;
275562306a36Sopenharmony_ci	int rc = 0;
275662306a36Sopenharmony_ci	struct ibmvscsis_nexus *nexus;
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	nexus = vscsi->tport.ibmv_nexus;
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci	cmd->rsp.sol_not = srp_tsk->sol_not;
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	switch (srp_tsk->tsk_mgmt_func) {
276362306a36Sopenharmony_ci	case SRP_TSK_ABORT_TASK:
276462306a36Sopenharmony_ci		tcm_type = TMR_ABORT_TASK;
276562306a36Sopenharmony_ci		tag_to_abort = be64_to_cpu(srp_tsk->task_tag);
276662306a36Sopenharmony_ci		break;
276762306a36Sopenharmony_ci	case SRP_TSK_ABORT_TASK_SET:
276862306a36Sopenharmony_ci		tcm_type = TMR_ABORT_TASK_SET;
276962306a36Sopenharmony_ci		break;
277062306a36Sopenharmony_ci	case SRP_TSK_CLEAR_TASK_SET:
277162306a36Sopenharmony_ci		tcm_type = TMR_CLEAR_TASK_SET;
277262306a36Sopenharmony_ci		break;
277362306a36Sopenharmony_ci	case SRP_TSK_LUN_RESET:
277462306a36Sopenharmony_ci		tcm_type = TMR_LUN_RESET;
277562306a36Sopenharmony_ci		break;
277662306a36Sopenharmony_ci	case SRP_TSK_CLEAR_ACA:
277762306a36Sopenharmony_ci		tcm_type = TMR_CLEAR_ACA;
277862306a36Sopenharmony_ci		break;
277962306a36Sopenharmony_ci	default:
278062306a36Sopenharmony_ci		dev_err(&vscsi->dev, "unknown task mgmt func %d\n",
278162306a36Sopenharmony_ci			srp_tsk->tsk_mgmt_func);
278262306a36Sopenharmony_ci		cmd->se_cmd.se_tmr_req->response =
278362306a36Sopenharmony_ci			TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED;
278462306a36Sopenharmony_ci		rc = -1;
278562306a36Sopenharmony_ci		break;
278662306a36Sopenharmony_ci	}
278762306a36Sopenharmony_ci
278862306a36Sopenharmony_ci	if (!rc) {
278962306a36Sopenharmony_ci		cmd->se_cmd.tag = be64_to_cpu(srp_tsk->tag);
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci		spin_lock_bh(&vscsi->intr_lock);
279262306a36Sopenharmony_ci		list_add_tail(&cmd->list, &vscsi->active_q);
279362306a36Sopenharmony_ci		spin_unlock_bh(&vscsi->intr_lock);
279462306a36Sopenharmony_ci
279562306a36Sopenharmony_ci		srp_tsk->lun.scsi_lun[0] &= 0x3f;
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "calling submit_tmr, func %d\n",
279862306a36Sopenharmony_ci			srp_tsk->tsk_mgmt_func);
279962306a36Sopenharmony_ci		rc = target_submit_tmr(&cmd->se_cmd, nexus->se_sess, NULL,
280062306a36Sopenharmony_ci				       scsilun_to_int(&srp_tsk->lun), srp_tsk,
280162306a36Sopenharmony_ci				       tcm_type, GFP_KERNEL, tag_to_abort, 0);
280262306a36Sopenharmony_ci		if (rc) {
280362306a36Sopenharmony_ci			dev_err(&vscsi->dev, "target_submit_tmr failed, rc %d\n",
280462306a36Sopenharmony_ci				rc);
280562306a36Sopenharmony_ci			spin_lock_bh(&vscsi->intr_lock);
280662306a36Sopenharmony_ci			list_del(&cmd->list);
280762306a36Sopenharmony_ci			spin_unlock_bh(&vscsi->intr_lock);
280862306a36Sopenharmony_ci			cmd->se_cmd.se_tmr_req->response =
280962306a36Sopenharmony_ci				TMR_FUNCTION_REJECTED;
281062306a36Sopenharmony_ci		}
281162306a36Sopenharmony_ci	}
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci	if (rc)
281462306a36Sopenharmony_ci		transport_send_check_condition_and_sense(&cmd->se_cmd, 0, 0);
281562306a36Sopenharmony_ci}
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_cistatic void ibmvscsis_scheduler(struct work_struct *work)
281862306a36Sopenharmony_ci{
281962306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd = container_of(work, struct ibmvscsis_cmd,
282062306a36Sopenharmony_ci						 work);
282162306a36Sopenharmony_ci	struct scsi_info *vscsi = cmd->adapter;
282262306a36Sopenharmony_ci
282362306a36Sopenharmony_ci	spin_lock_bh(&vscsi->intr_lock);
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci	/* Remove from schedule_q */
282662306a36Sopenharmony_ci	list_del(&cmd->list);
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ci	/* Don't submit cmd if we're disconnecting */
282962306a36Sopenharmony_ci	if (vscsi->flags & (SCHEDULE_DISCONNECT | DISCONNECT_SCHEDULED)) {
283062306a36Sopenharmony_ci		ibmvscsis_free_cmd_resources(vscsi, cmd);
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ci		/* ibmvscsis_disconnect might be waiting for us */
283362306a36Sopenharmony_ci		if (list_empty(&vscsi->active_q) &&
283462306a36Sopenharmony_ci		    list_empty(&vscsi->schedule_q) &&
283562306a36Sopenharmony_ci		    (vscsi->flags & WAIT_FOR_IDLE)) {
283662306a36Sopenharmony_ci			vscsi->flags &= ~WAIT_FOR_IDLE;
283762306a36Sopenharmony_ci			complete(&vscsi->wait_idle);
283862306a36Sopenharmony_ci		}
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci		spin_unlock_bh(&vscsi->intr_lock);
284162306a36Sopenharmony_ci		return;
284262306a36Sopenharmony_ci	}
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci	spin_unlock_bh(&vscsi->intr_lock);
284562306a36Sopenharmony_ci
284662306a36Sopenharmony_ci	switch (cmd->type) {
284762306a36Sopenharmony_ci	case SCSI_CDB:
284862306a36Sopenharmony_ci		ibmvscsis_parse_cmd(vscsi, cmd);
284962306a36Sopenharmony_ci		break;
285062306a36Sopenharmony_ci	case TASK_MANAGEMENT:
285162306a36Sopenharmony_ci		ibmvscsis_parse_task(vscsi, cmd);
285262306a36Sopenharmony_ci		break;
285362306a36Sopenharmony_ci	default:
285462306a36Sopenharmony_ci		dev_err(&vscsi->dev, "scheduler, invalid cmd type %d\n",
285562306a36Sopenharmony_ci			cmd->type);
285662306a36Sopenharmony_ci		spin_lock_bh(&vscsi->intr_lock);
285762306a36Sopenharmony_ci		ibmvscsis_free_cmd_resources(vscsi, cmd);
285862306a36Sopenharmony_ci		spin_unlock_bh(&vscsi->intr_lock);
285962306a36Sopenharmony_ci		break;
286062306a36Sopenharmony_ci	}
286162306a36Sopenharmony_ci}
286262306a36Sopenharmony_ci
286362306a36Sopenharmony_cistatic int ibmvscsis_alloc_cmds(struct scsi_info *vscsi, int num)
286462306a36Sopenharmony_ci{
286562306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd;
286662306a36Sopenharmony_ci	int i;
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci	INIT_LIST_HEAD(&vscsi->free_cmd);
286962306a36Sopenharmony_ci	vscsi->cmd_pool = kcalloc(num, sizeof(struct ibmvscsis_cmd),
287062306a36Sopenharmony_ci				  GFP_KERNEL);
287162306a36Sopenharmony_ci	if (!vscsi->cmd_pool)
287262306a36Sopenharmony_ci		return -ENOMEM;
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci	for (i = 0, cmd = (struct ibmvscsis_cmd *)vscsi->cmd_pool; i < num;
287562306a36Sopenharmony_ci	     i++, cmd++) {
287662306a36Sopenharmony_ci		cmd->abort_cmd = NULL;
287762306a36Sopenharmony_ci		cmd->adapter = vscsi;
287862306a36Sopenharmony_ci		INIT_WORK(&cmd->work, ibmvscsis_scheduler);
287962306a36Sopenharmony_ci		list_add_tail(&cmd->list, &vscsi->free_cmd);
288062306a36Sopenharmony_ci	}
288162306a36Sopenharmony_ci
288262306a36Sopenharmony_ci	return 0;
288362306a36Sopenharmony_ci}
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_cistatic void ibmvscsis_free_cmds(struct scsi_info *vscsi)
288662306a36Sopenharmony_ci{
288762306a36Sopenharmony_ci	kfree(vscsi->cmd_pool);
288862306a36Sopenharmony_ci	vscsi->cmd_pool = NULL;
288962306a36Sopenharmony_ci	INIT_LIST_HEAD(&vscsi->free_cmd);
289062306a36Sopenharmony_ci}
289162306a36Sopenharmony_ci
289262306a36Sopenharmony_ci/**
289362306a36Sopenharmony_ci * ibmvscsis_service_wait_q() - Service Waiting Queue
289462306a36Sopenharmony_ci * @timer:	Pointer to timer which has expired
289562306a36Sopenharmony_ci *
289662306a36Sopenharmony_ci * This routine is called when the timer pops to service the waiting
289762306a36Sopenharmony_ci * queue. Elements on the queue have completed, their responses have been
289862306a36Sopenharmony_ci * copied to the client, but the client's response queue was full so
289962306a36Sopenharmony_ci * the queue message could not be sent. The routine grabs the proper locks
290062306a36Sopenharmony_ci * and calls send messages.
290162306a36Sopenharmony_ci *
290262306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
290362306a36Sopenharmony_ci *	called at interrupt level
290462306a36Sopenharmony_ci */
290562306a36Sopenharmony_cistatic enum hrtimer_restart ibmvscsis_service_wait_q(struct hrtimer *timer)
290662306a36Sopenharmony_ci{
290762306a36Sopenharmony_ci	struct timer_cb *p_timer = container_of(timer, struct timer_cb, timer);
290862306a36Sopenharmony_ci	struct scsi_info *vscsi = container_of(p_timer, struct scsi_info,
290962306a36Sopenharmony_ci					       rsp_q_timer);
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci	spin_lock_bh(&vscsi->intr_lock);
291262306a36Sopenharmony_ci	p_timer->timer_pops += 1;
291362306a36Sopenharmony_ci	p_timer->started = false;
291462306a36Sopenharmony_ci	ibmvscsis_send_messages(vscsi);
291562306a36Sopenharmony_ci	spin_unlock_bh(&vscsi->intr_lock);
291662306a36Sopenharmony_ci
291762306a36Sopenharmony_ci	return HRTIMER_NORESTART;
291862306a36Sopenharmony_ci}
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_cistatic long ibmvscsis_alloctimer(struct scsi_info *vscsi)
292162306a36Sopenharmony_ci{
292262306a36Sopenharmony_ci	struct timer_cb *p_timer;
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci	p_timer = &vscsi->rsp_q_timer;
292562306a36Sopenharmony_ci	hrtimer_init(&p_timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci	p_timer->timer.function = ibmvscsis_service_wait_q;
292862306a36Sopenharmony_ci	p_timer->started = false;
292962306a36Sopenharmony_ci	p_timer->timer_pops = 0;
293062306a36Sopenharmony_ci
293162306a36Sopenharmony_ci	return ADAPT_SUCCESS;
293262306a36Sopenharmony_ci}
293362306a36Sopenharmony_ci
293462306a36Sopenharmony_cistatic void ibmvscsis_freetimer(struct scsi_info *vscsi)
293562306a36Sopenharmony_ci{
293662306a36Sopenharmony_ci	struct timer_cb *p_timer;
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_ci	p_timer = &vscsi->rsp_q_timer;
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	(void)hrtimer_cancel(&p_timer->timer);
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci	p_timer->started = false;
294362306a36Sopenharmony_ci	p_timer->timer_pops = 0;
294462306a36Sopenharmony_ci}
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_cistatic irqreturn_t ibmvscsis_interrupt(int dummy, void *data)
294762306a36Sopenharmony_ci{
294862306a36Sopenharmony_ci	struct scsi_info *vscsi = data;
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci	vio_disable_interrupts(vscsi->dma_dev);
295162306a36Sopenharmony_ci	tasklet_schedule(&vscsi->work_task);
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	return IRQ_HANDLED;
295462306a36Sopenharmony_ci}
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci/**
295762306a36Sopenharmony_ci * ibmvscsis_enable_change_state() - Set new state based on enabled status
295862306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
295962306a36Sopenharmony_ci *
296062306a36Sopenharmony_ci * This function determines our new state now that we are enabled.  This
296162306a36Sopenharmony_ci * may involve sending an Init Complete message to the client.
296262306a36Sopenharmony_ci *
296362306a36Sopenharmony_ci * Must be called with interrupt lock held.
296462306a36Sopenharmony_ci */
296562306a36Sopenharmony_cistatic long ibmvscsis_enable_change_state(struct scsi_info *vscsi)
296662306a36Sopenharmony_ci{
296762306a36Sopenharmony_ci	int bytes;
296862306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
296962306a36Sopenharmony_ci
297062306a36Sopenharmony_ci	bytes = vscsi->cmd_q.size * PAGE_SIZE;
297162306a36Sopenharmony_ci	rc = h_reg_crq(vscsi->dds.unit_id, vscsi->cmd_q.crq_token, bytes);
297262306a36Sopenharmony_ci	if (rc == H_CLOSED || rc == H_SUCCESS) {
297362306a36Sopenharmony_ci		vscsi->state = WAIT_CONNECTION;
297462306a36Sopenharmony_ci		rc = ibmvscsis_establish_new_q(vscsi);
297562306a36Sopenharmony_ci	}
297662306a36Sopenharmony_ci
297762306a36Sopenharmony_ci	if (rc != ADAPT_SUCCESS) {
297862306a36Sopenharmony_ci		vscsi->state = ERR_DISCONNECTED;
297962306a36Sopenharmony_ci		vscsi->flags |= RESPONSE_Q_DOWN;
298062306a36Sopenharmony_ci	}
298162306a36Sopenharmony_ci
298262306a36Sopenharmony_ci	return rc;
298362306a36Sopenharmony_ci}
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci/**
298662306a36Sopenharmony_ci * ibmvscsis_create_command_q() - Create Command Queue
298762306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
298862306a36Sopenharmony_ci * @num_cmds:	Currently unused.  In the future, may be used to determine
298962306a36Sopenharmony_ci *		the size of the CRQ.
299062306a36Sopenharmony_ci *
299162306a36Sopenharmony_ci * Allocates memory for command queue maps remote memory into an ioba
299262306a36Sopenharmony_ci * initializes the command response queue
299362306a36Sopenharmony_ci *
299462306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
299562306a36Sopenharmony_ci *	Process level only
299662306a36Sopenharmony_ci */
299762306a36Sopenharmony_cistatic long ibmvscsis_create_command_q(struct scsi_info *vscsi, int num_cmds)
299862306a36Sopenharmony_ci{
299962306a36Sopenharmony_ci	int pages;
300062306a36Sopenharmony_ci	struct vio_dev *vdev = vscsi->dma_dev;
300162306a36Sopenharmony_ci
300262306a36Sopenharmony_ci	/* We might support multiple pages in the future, but just 1 for now */
300362306a36Sopenharmony_ci	pages = 1;
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci	vscsi->cmd_q.size = pages;
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_ci	vscsi->cmd_q.base_addr =
300862306a36Sopenharmony_ci		(struct viosrp_crq *)get_zeroed_page(GFP_KERNEL);
300962306a36Sopenharmony_ci	if (!vscsi->cmd_q.base_addr)
301062306a36Sopenharmony_ci		return -ENOMEM;
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	vscsi->cmd_q.mask = ((uint)pages * CRQ_PER_PAGE) - 1;
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_ci	vscsi->cmd_q.crq_token = dma_map_single(&vdev->dev,
301562306a36Sopenharmony_ci						vscsi->cmd_q.base_addr,
301662306a36Sopenharmony_ci						PAGE_SIZE, DMA_BIDIRECTIONAL);
301762306a36Sopenharmony_ci	if (dma_mapping_error(&vdev->dev, vscsi->cmd_q.crq_token)) {
301862306a36Sopenharmony_ci		free_page((unsigned long)vscsi->cmd_q.base_addr);
301962306a36Sopenharmony_ci		return -ENOMEM;
302062306a36Sopenharmony_ci	}
302162306a36Sopenharmony_ci
302262306a36Sopenharmony_ci	return 0;
302362306a36Sopenharmony_ci}
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_ci/**
302662306a36Sopenharmony_ci * ibmvscsis_destroy_command_q - Destroy Command Queue
302762306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
302862306a36Sopenharmony_ci *
302962306a36Sopenharmony_ci * Releases memory for command queue and unmaps mapped remote memory.
303062306a36Sopenharmony_ci *
303162306a36Sopenharmony_ci * EXECUTION ENVIRONMENT:
303262306a36Sopenharmony_ci *	Process level only
303362306a36Sopenharmony_ci */
303462306a36Sopenharmony_cistatic void ibmvscsis_destroy_command_q(struct scsi_info *vscsi)
303562306a36Sopenharmony_ci{
303662306a36Sopenharmony_ci	dma_unmap_single(&vscsi->dma_dev->dev, vscsi->cmd_q.crq_token,
303762306a36Sopenharmony_ci			 PAGE_SIZE, DMA_BIDIRECTIONAL);
303862306a36Sopenharmony_ci	free_page((unsigned long)vscsi->cmd_q.base_addr);
303962306a36Sopenharmony_ci	vscsi->cmd_q.base_addr = NULL;
304062306a36Sopenharmony_ci	vscsi->state = NO_QUEUE;
304162306a36Sopenharmony_ci}
304262306a36Sopenharmony_ci
304362306a36Sopenharmony_cistatic u8 ibmvscsis_fast_fail(struct scsi_info *vscsi,
304462306a36Sopenharmony_ci			      struct ibmvscsis_cmd *cmd)
304562306a36Sopenharmony_ci{
304662306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
304762306a36Sopenharmony_ci	struct se_cmd *se_cmd = &cmd->se_cmd;
304862306a36Sopenharmony_ci	struct srp_cmd *srp = (struct srp_cmd *)iue->sbuf->buf;
304962306a36Sopenharmony_ci	struct scsi_sense_hdr sshdr;
305062306a36Sopenharmony_ci	u8 rc = se_cmd->scsi_status;
305162306a36Sopenharmony_ci
305262306a36Sopenharmony_ci	if (vscsi->fast_fail && (READ_CMD(srp->cdb) || WRITE_CMD(srp->cdb)))
305362306a36Sopenharmony_ci		if (scsi_normalize_sense(se_cmd->sense_buffer,
305462306a36Sopenharmony_ci					 se_cmd->scsi_sense_length, &sshdr))
305562306a36Sopenharmony_ci			if (sshdr.sense_key == HARDWARE_ERROR &&
305662306a36Sopenharmony_ci			    (se_cmd->residual_count == 0 ||
305762306a36Sopenharmony_ci			     se_cmd->residual_count == se_cmd->data_length)) {
305862306a36Sopenharmony_ci				rc = NO_SENSE;
305962306a36Sopenharmony_ci				cmd->flags |= CMD_FAST_FAIL;
306062306a36Sopenharmony_ci			}
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_ci	return rc;
306362306a36Sopenharmony_ci}
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_ci/**
306662306a36Sopenharmony_ci * srp_build_response() - Build an SRP response buffer
306762306a36Sopenharmony_ci * @vscsi:	Pointer to our adapter structure
306862306a36Sopenharmony_ci * @cmd:	Pointer to command for which to send the response
306962306a36Sopenharmony_ci * @len_p:	Where to return the length of the IU response sent.  This
307062306a36Sopenharmony_ci *		is needed to construct the CRQ response.
307162306a36Sopenharmony_ci *
307262306a36Sopenharmony_ci * Build the SRP response buffer and copy it to the client's memory space.
307362306a36Sopenharmony_ci */
307462306a36Sopenharmony_cistatic long srp_build_response(struct scsi_info *vscsi,
307562306a36Sopenharmony_ci			       struct ibmvscsis_cmd *cmd, uint *len_p)
307662306a36Sopenharmony_ci{
307762306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
307862306a36Sopenharmony_ci	struct se_cmd *se_cmd = &cmd->se_cmd;
307962306a36Sopenharmony_ci	struct srp_rsp *rsp;
308062306a36Sopenharmony_ci	uint len;
308162306a36Sopenharmony_ci	u32 rsp_code;
308262306a36Sopenharmony_ci	char *data;
308362306a36Sopenharmony_ci	u32 *tsk_status;
308462306a36Sopenharmony_ci	long rc = ADAPT_SUCCESS;
308562306a36Sopenharmony_ci
308662306a36Sopenharmony_ci	spin_lock_bh(&vscsi->intr_lock);
308762306a36Sopenharmony_ci
308862306a36Sopenharmony_ci	rsp = &vio_iu(iue)->srp.rsp;
308962306a36Sopenharmony_ci	len = sizeof(*rsp);
309062306a36Sopenharmony_ci	memset(rsp, 0, len);
309162306a36Sopenharmony_ci	data = rsp->data;
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci	rsp->opcode = SRP_RSP;
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	rsp->req_lim_delta = cpu_to_be32(1 + vscsi->credit);
309662306a36Sopenharmony_ci	rsp->tag = cmd->rsp.tag;
309762306a36Sopenharmony_ci	rsp->flags = 0;
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_ci	if (cmd->type == SCSI_CDB) {
310062306a36Sopenharmony_ci		rsp->status = ibmvscsis_fast_fail(vscsi, cmd);
310162306a36Sopenharmony_ci		if (rsp->status) {
310262306a36Sopenharmony_ci			dev_dbg(&vscsi->dev, "build_resp: cmd %p, scsi status %d\n",
310362306a36Sopenharmony_ci				cmd, (int)rsp->status);
310462306a36Sopenharmony_ci			ibmvscsis_determine_resid(se_cmd, rsp);
310562306a36Sopenharmony_ci			if (se_cmd->scsi_sense_length && se_cmd->sense_buffer) {
310662306a36Sopenharmony_ci				rsp->sense_data_len =
310762306a36Sopenharmony_ci					cpu_to_be32(se_cmd->scsi_sense_length);
310862306a36Sopenharmony_ci				rsp->flags |= SRP_RSP_FLAG_SNSVALID;
310962306a36Sopenharmony_ci				len += se_cmd->scsi_sense_length;
311062306a36Sopenharmony_ci				memcpy(data, se_cmd->sense_buffer,
311162306a36Sopenharmony_ci				       se_cmd->scsi_sense_length);
311262306a36Sopenharmony_ci			}
311362306a36Sopenharmony_ci			rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >>
311462306a36Sopenharmony_ci				UCSOLNT_RESP_SHIFT;
311562306a36Sopenharmony_ci		} else if (cmd->flags & CMD_FAST_FAIL) {
311662306a36Sopenharmony_ci			dev_dbg(&vscsi->dev, "build_resp: cmd %p, fast fail\n",
311762306a36Sopenharmony_ci				cmd);
311862306a36Sopenharmony_ci			rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >>
311962306a36Sopenharmony_ci				UCSOLNT_RESP_SHIFT;
312062306a36Sopenharmony_ci		} else {
312162306a36Sopenharmony_ci			rsp->sol_not = (cmd->rsp.sol_not & SCSOLNT) >>
312262306a36Sopenharmony_ci				SCSOLNT_RESP_SHIFT;
312362306a36Sopenharmony_ci		}
312462306a36Sopenharmony_ci	} else {
312562306a36Sopenharmony_ci		/* this is task management */
312662306a36Sopenharmony_ci		rsp->status = 0;
312762306a36Sopenharmony_ci		rsp->resp_data_len = cpu_to_be32(4);
312862306a36Sopenharmony_ci		rsp->flags |= SRP_RSP_FLAG_RSPVALID;
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci		switch (se_cmd->se_tmr_req->response) {
313162306a36Sopenharmony_ci		case TMR_FUNCTION_COMPLETE:
313262306a36Sopenharmony_ci		case TMR_TASK_DOES_NOT_EXIST:
313362306a36Sopenharmony_ci			rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_COMPLETE;
313462306a36Sopenharmony_ci			rsp->sol_not = (cmd->rsp.sol_not & SCSOLNT) >>
313562306a36Sopenharmony_ci				SCSOLNT_RESP_SHIFT;
313662306a36Sopenharmony_ci			break;
313762306a36Sopenharmony_ci		case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED:
313862306a36Sopenharmony_ci		case TMR_LUN_DOES_NOT_EXIST:
313962306a36Sopenharmony_ci			rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_NOT_SUPPORTED;
314062306a36Sopenharmony_ci			rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >>
314162306a36Sopenharmony_ci				UCSOLNT_RESP_SHIFT;
314262306a36Sopenharmony_ci			break;
314362306a36Sopenharmony_ci		case TMR_FUNCTION_FAILED:
314462306a36Sopenharmony_ci		case TMR_FUNCTION_REJECTED:
314562306a36Sopenharmony_ci		default:
314662306a36Sopenharmony_ci			rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_FAILED;
314762306a36Sopenharmony_ci			rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >>
314862306a36Sopenharmony_ci				UCSOLNT_RESP_SHIFT;
314962306a36Sopenharmony_ci			break;
315062306a36Sopenharmony_ci		}
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci		tsk_status = (u32 *)data;
315362306a36Sopenharmony_ci		*tsk_status = cpu_to_be32(rsp_code);
315462306a36Sopenharmony_ci		data = (char *)(tsk_status + 1);
315562306a36Sopenharmony_ci		len += 4;
315662306a36Sopenharmony_ci	}
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_ci	dma_wmb();
315962306a36Sopenharmony_ci	rc = h_copy_rdma(len, vscsi->dds.window[LOCAL].liobn, iue->sbuf->dma,
316062306a36Sopenharmony_ci			 vscsi->dds.window[REMOTE].liobn,
316162306a36Sopenharmony_ci			 be64_to_cpu(iue->remote_token));
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci	switch (rc) {
316462306a36Sopenharmony_ci	case H_SUCCESS:
316562306a36Sopenharmony_ci		vscsi->credit = 0;
316662306a36Sopenharmony_ci		*len_p = len;
316762306a36Sopenharmony_ci		break;
316862306a36Sopenharmony_ci	case H_PERMISSION:
316962306a36Sopenharmony_ci		if (connection_broken(vscsi))
317062306a36Sopenharmony_ci			vscsi->flags |= RESPONSE_Q_DOWN | CLIENT_FAILED;
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci		dev_err(&vscsi->dev, "build_response: error copying to client, rc %ld, flags 0x%x, state 0x%hx\n",
317362306a36Sopenharmony_ci			rc, vscsi->flags, vscsi->state);
317462306a36Sopenharmony_ci		break;
317562306a36Sopenharmony_ci	case H_SOURCE_PARM:
317662306a36Sopenharmony_ci	case H_DEST_PARM:
317762306a36Sopenharmony_ci	default:
317862306a36Sopenharmony_ci		dev_err(&vscsi->dev, "build_response: error copying to client, rc %ld\n",
317962306a36Sopenharmony_ci			rc);
318062306a36Sopenharmony_ci		break;
318162306a36Sopenharmony_ci	}
318262306a36Sopenharmony_ci
318362306a36Sopenharmony_ci	spin_unlock_bh(&vscsi->intr_lock);
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ci	return rc;
318662306a36Sopenharmony_ci}
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_cistatic int ibmvscsis_rdma(struct ibmvscsis_cmd *cmd, struct scatterlist *sg,
318962306a36Sopenharmony_ci			  int nsg, struct srp_direct_buf *md, int nmd,
319062306a36Sopenharmony_ci			  enum dma_data_direction dir, unsigned int bytes)
319162306a36Sopenharmony_ci{
319262306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
319362306a36Sopenharmony_ci	struct srp_target *target = iue->target;
319462306a36Sopenharmony_ci	struct scsi_info *vscsi = target->ldata;
319562306a36Sopenharmony_ci	struct scatterlist *sgp;
319662306a36Sopenharmony_ci	dma_addr_t client_ioba, server_ioba;
319762306a36Sopenharmony_ci	ulong buf_len;
319862306a36Sopenharmony_ci	ulong client_len, server_len;
319962306a36Sopenharmony_ci	int md_idx;
320062306a36Sopenharmony_ci	long tx_len;
320162306a36Sopenharmony_ci	long rc = 0;
320262306a36Sopenharmony_ci
320362306a36Sopenharmony_ci	if (bytes == 0)
320462306a36Sopenharmony_ci		return 0;
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_ci	sgp = sg;
320762306a36Sopenharmony_ci	client_len = 0;
320862306a36Sopenharmony_ci	server_len = 0;
320962306a36Sopenharmony_ci	md_idx = 0;
321062306a36Sopenharmony_ci	tx_len = bytes;
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	do {
321362306a36Sopenharmony_ci		if (client_len == 0) {
321462306a36Sopenharmony_ci			if (md_idx >= nmd) {
321562306a36Sopenharmony_ci				dev_err(&vscsi->dev, "rdma: ran out of client memory descriptors\n");
321662306a36Sopenharmony_ci				rc = -EIO;
321762306a36Sopenharmony_ci				break;
321862306a36Sopenharmony_ci			}
321962306a36Sopenharmony_ci			client_ioba = be64_to_cpu(md[md_idx].va);
322062306a36Sopenharmony_ci			client_len = be32_to_cpu(md[md_idx].len);
322162306a36Sopenharmony_ci		}
322262306a36Sopenharmony_ci		if (server_len == 0) {
322362306a36Sopenharmony_ci			if (!sgp) {
322462306a36Sopenharmony_ci				dev_err(&vscsi->dev, "rdma: ran out of scatter/gather list\n");
322562306a36Sopenharmony_ci				rc = -EIO;
322662306a36Sopenharmony_ci				break;
322762306a36Sopenharmony_ci			}
322862306a36Sopenharmony_ci			server_ioba = sg_dma_address(sgp);
322962306a36Sopenharmony_ci			server_len = sg_dma_len(sgp);
323062306a36Sopenharmony_ci		}
323162306a36Sopenharmony_ci
323262306a36Sopenharmony_ci		buf_len = tx_len;
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_ci		if (buf_len > client_len)
323562306a36Sopenharmony_ci			buf_len = client_len;
323662306a36Sopenharmony_ci
323762306a36Sopenharmony_ci		if (buf_len > server_len)
323862306a36Sopenharmony_ci			buf_len = server_len;
323962306a36Sopenharmony_ci
324062306a36Sopenharmony_ci		if (buf_len > max_vdma_size)
324162306a36Sopenharmony_ci			buf_len = max_vdma_size;
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci		if (dir == DMA_TO_DEVICE) {
324462306a36Sopenharmony_ci			/* read from client */
324562306a36Sopenharmony_ci			rc = h_copy_rdma(buf_len,
324662306a36Sopenharmony_ci					 vscsi->dds.window[REMOTE].liobn,
324762306a36Sopenharmony_ci					 client_ioba,
324862306a36Sopenharmony_ci					 vscsi->dds.window[LOCAL].liobn,
324962306a36Sopenharmony_ci					 server_ioba);
325062306a36Sopenharmony_ci		} else {
325162306a36Sopenharmony_ci			/* The h_copy_rdma will cause phyp, running in another
325262306a36Sopenharmony_ci			 * partition, to read memory, so we need to make sure
325362306a36Sopenharmony_ci			 * the data has been written out, hence these syncs.
325462306a36Sopenharmony_ci			 */
325562306a36Sopenharmony_ci			/* ensure that everything is in memory */
325662306a36Sopenharmony_ci			isync();
325762306a36Sopenharmony_ci			/* ensure that memory has been made visible */
325862306a36Sopenharmony_ci			dma_wmb();
325962306a36Sopenharmony_ci			rc = h_copy_rdma(buf_len,
326062306a36Sopenharmony_ci					 vscsi->dds.window[LOCAL].liobn,
326162306a36Sopenharmony_ci					 server_ioba,
326262306a36Sopenharmony_ci					 vscsi->dds.window[REMOTE].liobn,
326362306a36Sopenharmony_ci					 client_ioba);
326462306a36Sopenharmony_ci		}
326562306a36Sopenharmony_ci		switch (rc) {
326662306a36Sopenharmony_ci		case H_SUCCESS:
326762306a36Sopenharmony_ci			break;
326862306a36Sopenharmony_ci		case H_PERMISSION:
326962306a36Sopenharmony_ci		case H_SOURCE_PARM:
327062306a36Sopenharmony_ci		case H_DEST_PARM:
327162306a36Sopenharmony_ci			if (connection_broken(vscsi)) {
327262306a36Sopenharmony_ci				spin_lock_bh(&vscsi->intr_lock);
327362306a36Sopenharmony_ci				vscsi->flags |=
327462306a36Sopenharmony_ci					(RESPONSE_Q_DOWN | CLIENT_FAILED);
327562306a36Sopenharmony_ci				spin_unlock_bh(&vscsi->intr_lock);
327662306a36Sopenharmony_ci			}
327762306a36Sopenharmony_ci			dev_err(&vscsi->dev, "rdma: h_copy_rdma failed, rc %ld\n",
327862306a36Sopenharmony_ci				rc);
327962306a36Sopenharmony_ci			break;
328062306a36Sopenharmony_ci
328162306a36Sopenharmony_ci		default:
328262306a36Sopenharmony_ci			dev_err(&vscsi->dev, "rdma: unknown error %ld from h_copy_rdma\n",
328362306a36Sopenharmony_ci				rc);
328462306a36Sopenharmony_ci			break;
328562306a36Sopenharmony_ci		}
328662306a36Sopenharmony_ci
328762306a36Sopenharmony_ci		if (!rc) {
328862306a36Sopenharmony_ci			tx_len -= buf_len;
328962306a36Sopenharmony_ci			if (tx_len) {
329062306a36Sopenharmony_ci				client_len -= buf_len;
329162306a36Sopenharmony_ci				if (client_len == 0)
329262306a36Sopenharmony_ci					md_idx++;
329362306a36Sopenharmony_ci				else
329462306a36Sopenharmony_ci					client_ioba += buf_len;
329562306a36Sopenharmony_ci
329662306a36Sopenharmony_ci				server_len -= buf_len;
329762306a36Sopenharmony_ci				if (server_len == 0)
329862306a36Sopenharmony_ci					sgp = sg_next(sgp);
329962306a36Sopenharmony_ci				else
330062306a36Sopenharmony_ci					server_ioba += buf_len;
330162306a36Sopenharmony_ci			} else {
330262306a36Sopenharmony_ci				break;
330362306a36Sopenharmony_ci			}
330462306a36Sopenharmony_ci		}
330562306a36Sopenharmony_ci	} while (!rc);
330662306a36Sopenharmony_ci
330762306a36Sopenharmony_ci	return rc;
330862306a36Sopenharmony_ci}
330962306a36Sopenharmony_ci
331062306a36Sopenharmony_ci/**
331162306a36Sopenharmony_ci * ibmvscsis_handle_crq() - Handle CRQ
331262306a36Sopenharmony_ci * @data:	Pointer to our adapter structure
331362306a36Sopenharmony_ci *
331462306a36Sopenharmony_ci * Read the command elements from the command queue and copy the payloads
331562306a36Sopenharmony_ci * associated with the command elements to local memory and execute the
331662306a36Sopenharmony_ci * SRP requests.
331762306a36Sopenharmony_ci *
331862306a36Sopenharmony_ci * Note: this is an edge triggered interrupt. It can not be shared.
331962306a36Sopenharmony_ci */
332062306a36Sopenharmony_cistatic void ibmvscsis_handle_crq(unsigned long data)
332162306a36Sopenharmony_ci{
332262306a36Sopenharmony_ci	struct scsi_info *vscsi = (struct scsi_info *)data;
332362306a36Sopenharmony_ci	struct viosrp_crq *crq;
332462306a36Sopenharmony_ci	long rc;
332562306a36Sopenharmony_ci	bool ack = true;
332662306a36Sopenharmony_ci	volatile u8 valid;
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_ci	spin_lock_bh(&vscsi->intr_lock);
332962306a36Sopenharmony_ci
333062306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "got interrupt\n");
333162306a36Sopenharmony_ci
333262306a36Sopenharmony_ci	/*
333362306a36Sopenharmony_ci	 * if we are in a path where we are waiting for all pending commands
333462306a36Sopenharmony_ci	 * to complete because we received a transport event and anything in
333562306a36Sopenharmony_ci	 * the command queue is for a new connection, do nothing
333662306a36Sopenharmony_ci	 */
333762306a36Sopenharmony_ci	if (TARGET_STOP(vscsi)) {
333862306a36Sopenharmony_ci		vio_enable_interrupts(vscsi->dma_dev);
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "handle_crq, don't process: flags 0x%x, state 0x%hx\n",
334162306a36Sopenharmony_ci			vscsi->flags, vscsi->state);
334262306a36Sopenharmony_ci		spin_unlock_bh(&vscsi->intr_lock);
334362306a36Sopenharmony_ci		return;
334462306a36Sopenharmony_ci	}
334562306a36Sopenharmony_ci
334662306a36Sopenharmony_ci	rc = vscsi->flags & SCHEDULE_DISCONNECT;
334762306a36Sopenharmony_ci	crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index;
334862306a36Sopenharmony_ci	valid = crq->valid;
334962306a36Sopenharmony_ci	dma_rmb();
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_ci	while (valid) {
335262306a36Sopenharmony_ci		/*
335362306a36Sopenharmony_ci		 * These are edege triggered interrupts. After dropping out of
335462306a36Sopenharmony_ci		 * the while loop, the code must check for work since an
335562306a36Sopenharmony_ci		 * interrupt could be lost, and an elment be left on the queue,
335662306a36Sopenharmony_ci		 * hence the label.
335762306a36Sopenharmony_ci		 */
335862306a36Sopenharmony_cicmd_work:
335962306a36Sopenharmony_ci		vscsi->cmd_q.index =
336062306a36Sopenharmony_ci			(vscsi->cmd_q.index + 1) & vscsi->cmd_q.mask;
336162306a36Sopenharmony_ci
336262306a36Sopenharmony_ci		if (!rc) {
336362306a36Sopenharmony_ci			rc = ibmvscsis_parse_command(vscsi, crq);
336462306a36Sopenharmony_ci		} else {
336562306a36Sopenharmony_ci			if ((uint)crq->valid == VALID_TRANS_EVENT) {
336662306a36Sopenharmony_ci				/*
336762306a36Sopenharmony_ci				 * must service the transport layer events even
336862306a36Sopenharmony_ci				 * in an error state, dont break out until all
336962306a36Sopenharmony_ci				 * the consecutive transport events have been
337062306a36Sopenharmony_ci				 * processed
337162306a36Sopenharmony_ci				 */
337262306a36Sopenharmony_ci				rc = ibmvscsis_trans_event(vscsi, crq);
337362306a36Sopenharmony_ci			} else if (vscsi->flags & TRANS_EVENT) {
337462306a36Sopenharmony_ci				/*
337562306a36Sopenharmony_ci				 * if a transport event has occurred leave
337662306a36Sopenharmony_ci				 * everything but transport events on the queue
337762306a36Sopenharmony_ci				 *
337862306a36Sopenharmony_ci				 * need to decrement the queue index so we can
337962306a36Sopenharmony_ci				 * look at the element again
338062306a36Sopenharmony_ci				 */
338162306a36Sopenharmony_ci				if (vscsi->cmd_q.index)
338262306a36Sopenharmony_ci					vscsi->cmd_q.index -= 1;
338362306a36Sopenharmony_ci				else
338462306a36Sopenharmony_ci					/*
338562306a36Sopenharmony_ci					 * index is at 0 it just wrapped.
338662306a36Sopenharmony_ci					 * have it index last element in q
338762306a36Sopenharmony_ci					 */
338862306a36Sopenharmony_ci					vscsi->cmd_q.index = vscsi->cmd_q.mask;
338962306a36Sopenharmony_ci				break;
339062306a36Sopenharmony_ci			}
339162306a36Sopenharmony_ci		}
339262306a36Sopenharmony_ci
339362306a36Sopenharmony_ci		crq->valid = INVALIDATE_CMD_RESP_EL;
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci		crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index;
339662306a36Sopenharmony_ci		valid = crq->valid;
339762306a36Sopenharmony_ci		dma_rmb();
339862306a36Sopenharmony_ci	}
339962306a36Sopenharmony_ci
340062306a36Sopenharmony_ci	if (!rc) {
340162306a36Sopenharmony_ci		if (ack) {
340262306a36Sopenharmony_ci			vio_enable_interrupts(vscsi->dma_dev);
340362306a36Sopenharmony_ci			ack = false;
340462306a36Sopenharmony_ci			dev_dbg(&vscsi->dev, "handle_crq, reenabling interrupts\n");
340562306a36Sopenharmony_ci		}
340662306a36Sopenharmony_ci		valid = crq->valid;
340762306a36Sopenharmony_ci		dma_rmb();
340862306a36Sopenharmony_ci		if (valid)
340962306a36Sopenharmony_ci			goto cmd_work;
341062306a36Sopenharmony_ci	} else {
341162306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "handle_crq, error: flags 0x%x, state 0x%hx, crq index 0x%x\n",
341262306a36Sopenharmony_ci			vscsi->flags, vscsi->state, vscsi->cmd_q.index);
341362306a36Sopenharmony_ci	}
341462306a36Sopenharmony_ci
341562306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "Leaving handle_crq: schedule_q empty %d, flags 0x%x, state 0x%hx\n",
341662306a36Sopenharmony_ci		(int)list_empty(&vscsi->schedule_q), vscsi->flags,
341762306a36Sopenharmony_ci		vscsi->state);
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_ci	spin_unlock_bh(&vscsi->intr_lock);
342062306a36Sopenharmony_ci}
342162306a36Sopenharmony_ci
342262306a36Sopenharmony_cistatic int ibmvscsis_probe(struct vio_dev *vdev,
342362306a36Sopenharmony_ci			   const struct vio_device_id *id)
342462306a36Sopenharmony_ci{
342562306a36Sopenharmony_ci	struct scsi_info *vscsi;
342662306a36Sopenharmony_ci	int rc = 0;
342762306a36Sopenharmony_ci	long hrc = 0;
342862306a36Sopenharmony_ci	char wq_name[24];
342962306a36Sopenharmony_ci
343062306a36Sopenharmony_ci	vscsi = kzalloc(sizeof(*vscsi), GFP_KERNEL);
343162306a36Sopenharmony_ci	if (!vscsi) {
343262306a36Sopenharmony_ci		rc = -ENOMEM;
343362306a36Sopenharmony_ci		dev_err(&vdev->dev, "probe: allocation of adapter failed\n");
343462306a36Sopenharmony_ci		return rc;
343562306a36Sopenharmony_ci	}
343662306a36Sopenharmony_ci
343762306a36Sopenharmony_ci	vscsi->dma_dev = vdev;
343862306a36Sopenharmony_ci	vscsi->dev = vdev->dev;
343962306a36Sopenharmony_ci	INIT_LIST_HEAD(&vscsi->schedule_q);
344062306a36Sopenharmony_ci	INIT_LIST_HEAD(&vscsi->waiting_rsp);
344162306a36Sopenharmony_ci	INIT_LIST_HEAD(&vscsi->active_q);
344262306a36Sopenharmony_ci
344362306a36Sopenharmony_ci	snprintf(vscsi->tport.tport_name, IBMVSCSIS_NAMELEN, "%s",
344462306a36Sopenharmony_ci		 dev_name(&vdev->dev));
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "probe tport_name: %s\n", vscsi->tport.tport_name);
344762306a36Sopenharmony_ci
344862306a36Sopenharmony_ci	rc = read_dma_window(vscsi);
344962306a36Sopenharmony_ci	if (rc)
345062306a36Sopenharmony_ci		goto free_adapter;
345162306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "Probe: liobn 0x%x, riobn 0x%x\n",
345262306a36Sopenharmony_ci		vscsi->dds.window[LOCAL].liobn,
345362306a36Sopenharmony_ci		vscsi->dds.window[REMOTE].liobn);
345462306a36Sopenharmony_ci
345562306a36Sopenharmony_ci	snprintf(vscsi->eye, sizeof(vscsi->eye), "VSCSI %s", vdev->name);
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_ci	vscsi->dds.unit_id = vdev->unit_address;
345862306a36Sopenharmony_ci	strscpy(vscsi->dds.partition_name, partition_name,
345962306a36Sopenharmony_ci		sizeof(vscsi->dds.partition_name));
346062306a36Sopenharmony_ci	vscsi->dds.partition_num = partition_number;
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_ci	spin_lock_bh(&ibmvscsis_dev_lock);
346362306a36Sopenharmony_ci	list_add_tail(&vscsi->list, &ibmvscsis_dev_list);
346462306a36Sopenharmony_ci	spin_unlock_bh(&ibmvscsis_dev_lock);
346562306a36Sopenharmony_ci
346662306a36Sopenharmony_ci	/*
346762306a36Sopenharmony_ci	 * TBD: How do we determine # of cmds to request?  Do we know how
346862306a36Sopenharmony_ci	 * many "children" we have?
346962306a36Sopenharmony_ci	 */
347062306a36Sopenharmony_ci	vscsi->request_limit = INITIAL_SRP_LIMIT;
347162306a36Sopenharmony_ci	rc = srp_target_alloc(&vscsi->target, &vdev->dev, vscsi->request_limit,
347262306a36Sopenharmony_ci			      SRP_MAX_IU_LEN);
347362306a36Sopenharmony_ci	if (rc)
347462306a36Sopenharmony_ci		goto rem_list;
347562306a36Sopenharmony_ci
347662306a36Sopenharmony_ci	vscsi->target.ldata = vscsi;
347762306a36Sopenharmony_ci
347862306a36Sopenharmony_ci	rc = ibmvscsis_alloc_cmds(vscsi, vscsi->request_limit);
347962306a36Sopenharmony_ci	if (rc) {
348062306a36Sopenharmony_ci		dev_err(&vscsi->dev, "alloc_cmds failed, rc %d, num %d\n",
348162306a36Sopenharmony_ci			rc, vscsi->request_limit);
348262306a36Sopenharmony_ci		goto free_target;
348362306a36Sopenharmony_ci	}
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci	/*
348662306a36Sopenharmony_ci	 * Note: the lock is used in freeing timers, so must initialize
348762306a36Sopenharmony_ci	 * first so that ordering in case of error is correct.
348862306a36Sopenharmony_ci	 */
348962306a36Sopenharmony_ci	spin_lock_init(&vscsi->intr_lock);
349062306a36Sopenharmony_ci
349162306a36Sopenharmony_ci	rc = ibmvscsis_alloctimer(vscsi);
349262306a36Sopenharmony_ci	if (rc) {
349362306a36Sopenharmony_ci		dev_err(&vscsi->dev, "probe: alloctimer failed, rc %d\n", rc);
349462306a36Sopenharmony_ci		goto free_cmds;
349562306a36Sopenharmony_ci	}
349662306a36Sopenharmony_ci
349762306a36Sopenharmony_ci	rc = ibmvscsis_create_command_q(vscsi, 256);
349862306a36Sopenharmony_ci	if (rc) {
349962306a36Sopenharmony_ci		dev_err(&vscsi->dev, "probe: create_command_q failed, rc %d\n",
350062306a36Sopenharmony_ci			rc);
350162306a36Sopenharmony_ci		goto free_timer;
350262306a36Sopenharmony_ci	}
350362306a36Sopenharmony_ci
350462306a36Sopenharmony_ci	vscsi->map_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
350562306a36Sopenharmony_ci	if (!vscsi->map_buf) {
350662306a36Sopenharmony_ci		rc = -ENOMEM;
350762306a36Sopenharmony_ci		dev_err(&vscsi->dev, "probe: allocating cmd buffer failed\n");
350862306a36Sopenharmony_ci		goto destroy_queue;
350962306a36Sopenharmony_ci	}
351062306a36Sopenharmony_ci
351162306a36Sopenharmony_ci	vscsi->map_ioba = dma_map_single(&vdev->dev, vscsi->map_buf, PAGE_SIZE,
351262306a36Sopenharmony_ci					 DMA_BIDIRECTIONAL);
351362306a36Sopenharmony_ci	if (dma_mapping_error(&vdev->dev, vscsi->map_ioba)) {
351462306a36Sopenharmony_ci		rc = -ENOMEM;
351562306a36Sopenharmony_ci		dev_err(&vscsi->dev, "probe: error mapping command buffer\n");
351662306a36Sopenharmony_ci		goto free_buf;
351762306a36Sopenharmony_ci	}
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_ci	hrc = h_vioctl(vscsi->dds.unit_id, H_GET_PARTNER_INFO,
352062306a36Sopenharmony_ci		       (u64)vscsi->map_ioba | ((u64)PAGE_SIZE << 32), 0, 0, 0,
352162306a36Sopenharmony_ci		       0);
352262306a36Sopenharmony_ci	if (hrc == H_SUCCESS)
352362306a36Sopenharmony_ci		vscsi->client_data.partition_number =
352462306a36Sopenharmony_ci			be64_to_cpu(*(u64 *)vscsi->map_buf);
352562306a36Sopenharmony_ci	/*
352662306a36Sopenharmony_ci	 * We expect the VIOCTL to fail if we're configured as "any
352762306a36Sopenharmony_ci	 * client can connect" and the client isn't activated yet.
352862306a36Sopenharmony_ci	 * We'll make the call again when he sends an init msg.
352962306a36Sopenharmony_ci	 */
353062306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "probe hrc %ld, client partition num %d\n",
353162306a36Sopenharmony_ci		hrc, vscsi->client_data.partition_number);
353262306a36Sopenharmony_ci
353362306a36Sopenharmony_ci	tasklet_init(&vscsi->work_task, ibmvscsis_handle_crq,
353462306a36Sopenharmony_ci		     (unsigned long)vscsi);
353562306a36Sopenharmony_ci
353662306a36Sopenharmony_ci	init_completion(&vscsi->wait_idle);
353762306a36Sopenharmony_ci	init_completion(&vscsi->unconfig);
353862306a36Sopenharmony_ci
353962306a36Sopenharmony_ci	snprintf(wq_name, 24, "ibmvscsis%s", dev_name(&vdev->dev));
354062306a36Sopenharmony_ci	vscsi->work_q = create_workqueue(wq_name);
354162306a36Sopenharmony_ci	if (!vscsi->work_q) {
354262306a36Sopenharmony_ci		rc = -ENOMEM;
354362306a36Sopenharmony_ci		dev_err(&vscsi->dev, "create_workqueue failed\n");
354462306a36Sopenharmony_ci		goto unmap_buf;
354562306a36Sopenharmony_ci	}
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_ci	rc = request_irq(vdev->irq, ibmvscsis_interrupt, 0, "ibmvscsis", vscsi);
354862306a36Sopenharmony_ci	if (rc) {
354962306a36Sopenharmony_ci		rc = -EPERM;
355062306a36Sopenharmony_ci		dev_err(&vscsi->dev, "probe: request_irq failed, rc %d\n", rc);
355162306a36Sopenharmony_ci		goto destroy_WQ;
355262306a36Sopenharmony_ci	}
355362306a36Sopenharmony_ci
355462306a36Sopenharmony_ci	vscsi->state = WAIT_ENABLED;
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	dev_set_drvdata(&vdev->dev, vscsi);
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_ci	return 0;
355962306a36Sopenharmony_ci
356062306a36Sopenharmony_cidestroy_WQ:
356162306a36Sopenharmony_ci	destroy_workqueue(vscsi->work_q);
356262306a36Sopenharmony_ciunmap_buf:
356362306a36Sopenharmony_ci	dma_unmap_single(&vdev->dev, vscsi->map_ioba, PAGE_SIZE,
356462306a36Sopenharmony_ci			 DMA_BIDIRECTIONAL);
356562306a36Sopenharmony_cifree_buf:
356662306a36Sopenharmony_ci	kfree(vscsi->map_buf);
356762306a36Sopenharmony_cidestroy_queue:
356862306a36Sopenharmony_ci	tasklet_kill(&vscsi->work_task);
356962306a36Sopenharmony_ci	ibmvscsis_unregister_command_q(vscsi);
357062306a36Sopenharmony_ci	ibmvscsis_destroy_command_q(vscsi);
357162306a36Sopenharmony_cifree_timer:
357262306a36Sopenharmony_ci	ibmvscsis_freetimer(vscsi);
357362306a36Sopenharmony_cifree_cmds:
357462306a36Sopenharmony_ci	ibmvscsis_free_cmds(vscsi);
357562306a36Sopenharmony_cifree_target:
357662306a36Sopenharmony_ci	srp_target_free(&vscsi->target);
357762306a36Sopenharmony_cirem_list:
357862306a36Sopenharmony_ci	spin_lock_bh(&ibmvscsis_dev_lock);
357962306a36Sopenharmony_ci	list_del(&vscsi->list);
358062306a36Sopenharmony_ci	spin_unlock_bh(&ibmvscsis_dev_lock);
358162306a36Sopenharmony_cifree_adapter:
358262306a36Sopenharmony_ci	kfree(vscsi);
358362306a36Sopenharmony_ci
358462306a36Sopenharmony_ci	return rc;
358562306a36Sopenharmony_ci}
358662306a36Sopenharmony_ci
358762306a36Sopenharmony_cistatic void ibmvscsis_remove(struct vio_dev *vdev)
358862306a36Sopenharmony_ci{
358962306a36Sopenharmony_ci	struct scsi_info *vscsi = dev_get_drvdata(&vdev->dev);
359062306a36Sopenharmony_ci
359162306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "remove (%s)\n", dev_name(&vscsi->dma_dev->dev));
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_ci	spin_lock_bh(&vscsi->intr_lock);
359462306a36Sopenharmony_ci	ibmvscsis_post_disconnect(vscsi, UNCONFIGURING, 0);
359562306a36Sopenharmony_ci	vscsi->flags |= CFG_SLEEPING;
359662306a36Sopenharmony_ci	spin_unlock_bh(&vscsi->intr_lock);
359762306a36Sopenharmony_ci	wait_for_completion(&vscsi->unconfig);
359862306a36Sopenharmony_ci
359962306a36Sopenharmony_ci	vio_disable_interrupts(vdev);
360062306a36Sopenharmony_ci	free_irq(vdev->irq, vscsi);
360162306a36Sopenharmony_ci	destroy_workqueue(vscsi->work_q);
360262306a36Sopenharmony_ci	dma_unmap_single(&vdev->dev, vscsi->map_ioba, PAGE_SIZE,
360362306a36Sopenharmony_ci			 DMA_BIDIRECTIONAL);
360462306a36Sopenharmony_ci	kfree(vscsi->map_buf);
360562306a36Sopenharmony_ci	tasklet_kill(&vscsi->work_task);
360662306a36Sopenharmony_ci	ibmvscsis_destroy_command_q(vscsi);
360762306a36Sopenharmony_ci	ibmvscsis_freetimer(vscsi);
360862306a36Sopenharmony_ci	ibmvscsis_free_cmds(vscsi);
360962306a36Sopenharmony_ci	srp_target_free(&vscsi->target);
361062306a36Sopenharmony_ci	spin_lock_bh(&ibmvscsis_dev_lock);
361162306a36Sopenharmony_ci	list_del(&vscsi->list);
361262306a36Sopenharmony_ci	spin_unlock_bh(&ibmvscsis_dev_lock);
361362306a36Sopenharmony_ci	kfree(vscsi);
361462306a36Sopenharmony_ci}
361562306a36Sopenharmony_ci
361662306a36Sopenharmony_cistatic ssize_t system_id_show(struct device *dev,
361762306a36Sopenharmony_ci			      struct device_attribute *attr, char *buf)
361862306a36Sopenharmony_ci{
361962306a36Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%s\n", system_id);
362062306a36Sopenharmony_ci}
362162306a36Sopenharmony_ci
362262306a36Sopenharmony_cistatic ssize_t partition_number_show(struct device *dev,
362362306a36Sopenharmony_ci				     struct device_attribute *attr, char *buf)
362462306a36Sopenharmony_ci{
362562306a36Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%x\n", partition_number);
362662306a36Sopenharmony_ci}
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_cistatic ssize_t unit_address_show(struct device *dev,
362962306a36Sopenharmony_ci				 struct device_attribute *attr, char *buf)
363062306a36Sopenharmony_ci{
363162306a36Sopenharmony_ci	struct scsi_info *vscsi = container_of(dev, struct scsi_info, dev);
363262306a36Sopenharmony_ci
363362306a36Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%x\n", vscsi->dma_dev->unit_address);
363462306a36Sopenharmony_ci}
363562306a36Sopenharmony_ci
363662306a36Sopenharmony_cistatic int ibmvscsis_get_system_info(void)
363762306a36Sopenharmony_ci{
363862306a36Sopenharmony_ci	struct device_node *rootdn, *vdevdn;
363962306a36Sopenharmony_ci	const char *id, *model, *name;
364062306a36Sopenharmony_ci	const uint *num;
364162306a36Sopenharmony_ci
364262306a36Sopenharmony_ci	rootdn = of_find_node_by_path("/");
364362306a36Sopenharmony_ci	if (!rootdn)
364462306a36Sopenharmony_ci		return -ENOENT;
364562306a36Sopenharmony_ci
364662306a36Sopenharmony_ci	model = of_get_property(rootdn, "model", NULL);
364762306a36Sopenharmony_ci	id = of_get_property(rootdn, "system-id", NULL);
364862306a36Sopenharmony_ci	if (model && id)
364962306a36Sopenharmony_ci		snprintf(system_id, sizeof(system_id), "%s-%s", model, id);
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci	name = of_get_property(rootdn, "ibm,partition-name", NULL);
365262306a36Sopenharmony_ci	if (name)
365362306a36Sopenharmony_ci		strncpy(partition_name, name, sizeof(partition_name));
365462306a36Sopenharmony_ci
365562306a36Sopenharmony_ci	num = of_get_property(rootdn, "ibm,partition-no", NULL);
365662306a36Sopenharmony_ci	if (num)
365762306a36Sopenharmony_ci		partition_number = of_read_number(num, 1);
365862306a36Sopenharmony_ci
365962306a36Sopenharmony_ci	of_node_put(rootdn);
366062306a36Sopenharmony_ci
366162306a36Sopenharmony_ci	vdevdn = of_find_node_by_path("/vdevice");
366262306a36Sopenharmony_ci	if (vdevdn) {
366362306a36Sopenharmony_ci		const uint *mvds;
366462306a36Sopenharmony_ci
366562306a36Sopenharmony_ci		mvds = of_get_property(vdevdn, "ibm,max-virtual-dma-size",
366662306a36Sopenharmony_ci				       NULL);
366762306a36Sopenharmony_ci		if (mvds)
366862306a36Sopenharmony_ci			max_vdma_size = *mvds;
366962306a36Sopenharmony_ci		of_node_put(vdevdn);
367062306a36Sopenharmony_ci	}
367162306a36Sopenharmony_ci
367262306a36Sopenharmony_ci	return 0;
367362306a36Sopenharmony_ci}
367462306a36Sopenharmony_ci
367562306a36Sopenharmony_cistatic char *ibmvscsis_get_fabric_wwn(struct se_portal_group *se_tpg)
367662306a36Sopenharmony_ci{
367762306a36Sopenharmony_ci	struct ibmvscsis_tport *tport =
367862306a36Sopenharmony_ci		container_of(se_tpg, struct ibmvscsis_tport, se_tpg);
367962306a36Sopenharmony_ci
368062306a36Sopenharmony_ci	return tport->tport_name;
368162306a36Sopenharmony_ci}
368262306a36Sopenharmony_ci
368362306a36Sopenharmony_cistatic u16 ibmvscsis_get_tag(struct se_portal_group *se_tpg)
368462306a36Sopenharmony_ci{
368562306a36Sopenharmony_ci	struct ibmvscsis_tport *tport =
368662306a36Sopenharmony_ci		container_of(se_tpg, struct ibmvscsis_tport, se_tpg);
368762306a36Sopenharmony_ci
368862306a36Sopenharmony_ci	return tport->tport_tpgt;
368962306a36Sopenharmony_ci}
369062306a36Sopenharmony_ci
369162306a36Sopenharmony_cistatic u32 ibmvscsis_get_default_depth(struct se_portal_group *se_tpg)
369262306a36Sopenharmony_ci{
369362306a36Sopenharmony_ci	return 1;
369462306a36Sopenharmony_ci}
369562306a36Sopenharmony_ci
369662306a36Sopenharmony_cistatic int ibmvscsis_check_true(struct se_portal_group *se_tpg)
369762306a36Sopenharmony_ci{
369862306a36Sopenharmony_ci	return 1;
369962306a36Sopenharmony_ci}
370062306a36Sopenharmony_ci
370162306a36Sopenharmony_cistatic int ibmvscsis_check_stop_free(struct se_cmd *se_cmd)
370262306a36Sopenharmony_ci{
370362306a36Sopenharmony_ci	return target_put_sess_cmd(se_cmd);
370462306a36Sopenharmony_ci}
370562306a36Sopenharmony_ci
370662306a36Sopenharmony_cistatic void ibmvscsis_release_cmd(struct se_cmd *se_cmd)
370762306a36Sopenharmony_ci{
370862306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
370962306a36Sopenharmony_ci						 se_cmd);
371062306a36Sopenharmony_ci	struct scsi_info *vscsi = cmd->adapter;
371162306a36Sopenharmony_ci
371262306a36Sopenharmony_ci	spin_lock_bh(&vscsi->intr_lock);
371362306a36Sopenharmony_ci	/* Remove from active_q */
371462306a36Sopenharmony_ci	list_move_tail(&cmd->list, &vscsi->waiting_rsp);
371562306a36Sopenharmony_ci	ibmvscsis_send_messages(vscsi);
371662306a36Sopenharmony_ci	spin_unlock_bh(&vscsi->intr_lock);
371762306a36Sopenharmony_ci}
371862306a36Sopenharmony_ci
371962306a36Sopenharmony_cistatic int ibmvscsis_write_pending(struct se_cmd *se_cmd)
372062306a36Sopenharmony_ci{
372162306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
372262306a36Sopenharmony_ci						 se_cmd);
372362306a36Sopenharmony_ci	struct scsi_info *vscsi = cmd->adapter;
372462306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
372562306a36Sopenharmony_ci	int rc;
372662306a36Sopenharmony_ci
372762306a36Sopenharmony_ci	/*
372862306a36Sopenharmony_ci	 * If CLIENT_FAILED OR RESPONSE_Q_DOWN, then just return success
372962306a36Sopenharmony_ci	 * since LIO can't do anything about it, and we dont want to
373062306a36Sopenharmony_ci	 * attempt an srp_transfer_data.
373162306a36Sopenharmony_ci	 */
373262306a36Sopenharmony_ci	if ((vscsi->flags & (CLIENT_FAILED | RESPONSE_Q_DOWN))) {
373362306a36Sopenharmony_ci		dev_err(&vscsi->dev, "write_pending failed since: %d\n",
373462306a36Sopenharmony_ci			vscsi->flags);
373562306a36Sopenharmony_ci		return -EIO;
373662306a36Sopenharmony_ci
373762306a36Sopenharmony_ci	}
373862306a36Sopenharmony_ci
373962306a36Sopenharmony_ci	rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma,
374062306a36Sopenharmony_ci			       1, 1);
374162306a36Sopenharmony_ci	if (rc) {
374262306a36Sopenharmony_ci		dev_err(&vscsi->dev, "srp_transfer_data() failed: %d\n", rc);
374362306a36Sopenharmony_ci		return -EIO;
374462306a36Sopenharmony_ci	}
374562306a36Sopenharmony_ci	/*
374662306a36Sopenharmony_ci	 * We now tell TCM to add this WRITE CDB directly into the TCM storage
374762306a36Sopenharmony_ci	 * object execution queue.
374862306a36Sopenharmony_ci	 */
374962306a36Sopenharmony_ci	target_execute_cmd(se_cmd);
375062306a36Sopenharmony_ci	return 0;
375162306a36Sopenharmony_ci}
375262306a36Sopenharmony_ci
375362306a36Sopenharmony_cistatic int ibmvscsis_queue_data_in(struct se_cmd *se_cmd)
375462306a36Sopenharmony_ci{
375562306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
375662306a36Sopenharmony_ci						 se_cmd);
375762306a36Sopenharmony_ci	struct iu_entry *iue = cmd->iue;
375862306a36Sopenharmony_ci	struct scsi_info *vscsi = cmd->adapter;
375962306a36Sopenharmony_ci	uint len = 0;
376062306a36Sopenharmony_ci	int rc;
376162306a36Sopenharmony_ci
376262306a36Sopenharmony_ci	rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma, 1,
376362306a36Sopenharmony_ci			       1);
376462306a36Sopenharmony_ci	if (rc) {
376562306a36Sopenharmony_ci		dev_err(&vscsi->dev, "srp_transfer_data failed: %d\n", rc);
376662306a36Sopenharmony_ci		se_cmd->scsi_sense_length = 18;
376762306a36Sopenharmony_ci		memset(se_cmd->sense_buffer, 0, se_cmd->scsi_sense_length);
376862306a36Sopenharmony_ci		/* Logical Unit Communication Time-out asc/ascq = 0x0801 */
376962306a36Sopenharmony_ci		scsi_build_sense_buffer(0, se_cmd->sense_buffer, MEDIUM_ERROR,
377062306a36Sopenharmony_ci					0x08, 0x01);
377162306a36Sopenharmony_ci	}
377262306a36Sopenharmony_ci
377362306a36Sopenharmony_ci	srp_build_response(vscsi, cmd, &len);
377462306a36Sopenharmony_ci	cmd->rsp.format = SRP_FORMAT;
377562306a36Sopenharmony_ci	cmd->rsp.len = len;
377662306a36Sopenharmony_ci
377762306a36Sopenharmony_ci	return 0;
377862306a36Sopenharmony_ci}
377962306a36Sopenharmony_ci
378062306a36Sopenharmony_cistatic int ibmvscsis_queue_status(struct se_cmd *se_cmd)
378162306a36Sopenharmony_ci{
378262306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
378362306a36Sopenharmony_ci						 se_cmd);
378462306a36Sopenharmony_ci	struct scsi_info *vscsi = cmd->adapter;
378562306a36Sopenharmony_ci	uint len;
378662306a36Sopenharmony_ci
378762306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "queue_status %p\n", se_cmd);
378862306a36Sopenharmony_ci
378962306a36Sopenharmony_ci	srp_build_response(vscsi, cmd, &len);
379062306a36Sopenharmony_ci	cmd->rsp.format = SRP_FORMAT;
379162306a36Sopenharmony_ci	cmd->rsp.len = len;
379262306a36Sopenharmony_ci
379362306a36Sopenharmony_ci	return 0;
379462306a36Sopenharmony_ci}
379562306a36Sopenharmony_ci
379662306a36Sopenharmony_cistatic void ibmvscsis_queue_tm_rsp(struct se_cmd *se_cmd)
379762306a36Sopenharmony_ci{
379862306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
379962306a36Sopenharmony_ci						 se_cmd);
380062306a36Sopenharmony_ci	struct scsi_info *vscsi = cmd->adapter;
380162306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd_itr;
380262306a36Sopenharmony_ci	struct iu_entry *iue = iue = cmd->iue;
380362306a36Sopenharmony_ci	struct srp_tsk_mgmt *srp_tsk = &vio_iu(iue)->srp.tsk_mgmt;
380462306a36Sopenharmony_ci	u64 tag_to_abort = be64_to_cpu(srp_tsk->task_tag);
380562306a36Sopenharmony_ci	uint len;
380662306a36Sopenharmony_ci
380762306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "queue_tm_rsp %p, status %d\n",
380862306a36Sopenharmony_ci		se_cmd, (int)se_cmd->se_tmr_req->response);
380962306a36Sopenharmony_ci
381062306a36Sopenharmony_ci	if (srp_tsk->tsk_mgmt_func == SRP_TSK_ABORT_TASK &&
381162306a36Sopenharmony_ci	    cmd->se_cmd.se_tmr_req->response == TMR_TASK_DOES_NOT_EXIST) {
381262306a36Sopenharmony_ci		spin_lock_bh(&vscsi->intr_lock);
381362306a36Sopenharmony_ci		list_for_each_entry(cmd_itr, &vscsi->active_q, list) {
381462306a36Sopenharmony_ci			if (tag_to_abort == cmd_itr->se_cmd.tag) {
381562306a36Sopenharmony_ci				cmd_itr->abort_cmd = cmd;
381662306a36Sopenharmony_ci				cmd->flags |= DELAY_SEND;
381762306a36Sopenharmony_ci				break;
381862306a36Sopenharmony_ci			}
381962306a36Sopenharmony_ci		}
382062306a36Sopenharmony_ci		spin_unlock_bh(&vscsi->intr_lock);
382162306a36Sopenharmony_ci	}
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_ci	srp_build_response(vscsi, cmd, &len);
382462306a36Sopenharmony_ci	cmd->rsp.format = SRP_FORMAT;
382562306a36Sopenharmony_ci	cmd->rsp.len = len;
382662306a36Sopenharmony_ci}
382762306a36Sopenharmony_ci
382862306a36Sopenharmony_cistatic void ibmvscsis_aborted_task(struct se_cmd *se_cmd)
382962306a36Sopenharmony_ci{
383062306a36Sopenharmony_ci	struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd,
383162306a36Sopenharmony_ci						 se_cmd);
383262306a36Sopenharmony_ci	struct scsi_info *vscsi = cmd->adapter;
383362306a36Sopenharmony_ci
383462306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "ibmvscsis_aborted_task %p task_tag: %llu\n",
383562306a36Sopenharmony_ci		se_cmd, se_cmd->tag);
383662306a36Sopenharmony_ci}
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_cistatic struct se_wwn *ibmvscsis_make_tport(struct target_fabric_configfs *tf,
383962306a36Sopenharmony_ci					   struct config_group *group,
384062306a36Sopenharmony_ci					   const char *name)
384162306a36Sopenharmony_ci{
384262306a36Sopenharmony_ci	struct ibmvscsis_tport *tport;
384362306a36Sopenharmony_ci	struct scsi_info *vscsi;
384462306a36Sopenharmony_ci
384562306a36Sopenharmony_ci	tport = ibmvscsis_lookup_port(name);
384662306a36Sopenharmony_ci	if (tport) {
384762306a36Sopenharmony_ci		vscsi = container_of(tport, struct scsi_info, tport);
384862306a36Sopenharmony_ci		tport->tport_proto_id = SCSI_PROTOCOL_SRP;
384962306a36Sopenharmony_ci		dev_dbg(&vscsi->dev, "make_tport(%s), pointer:%p, tport_id:%x\n",
385062306a36Sopenharmony_ci			name, tport, tport->tport_proto_id);
385162306a36Sopenharmony_ci		return &tport->tport_wwn;
385262306a36Sopenharmony_ci	}
385362306a36Sopenharmony_ci
385462306a36Sopenharmony_ci	return ERR_PTR(-EINVAL);
385562306a36Sopenharmony_ci}
385662306a36Sopenharmony_ci
385762306a36Sopenharmony_cistatic void ibmvscsis_drop_tport(struct se_wwn *wwn)
385862306a36Sopenharmony_ci{
385962306a36Sopenharmony_ci	struct ibmvscsis_tport *tport = container_of(wwn,
386062306a36Sopenharmony_ci						     struct ibmvscsis_tport,
386162306a36Sopenharmony_ci						     tport_wwn);
386262306a36Sopenharmony_ci	struct scsi_info *vscsi = container_of(tport, struct scsi_info, tport);
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci	dev_dbg(&vscsi->dev, "drop_tport(%s)\n",
386562306a36Sopenharmony_ci		config_item_name(&tport->tport_wwn.wwn_group.cg_item));
386662306a36Sopenharmony_ci}
386762306a36Sopenharmony_ci
386862306a36Sopenharmony_cistatic struct se_portal_group *ibmvscsis_make_tpg(struct se_wwn *wwn,
386962306a36Sopenharmony_ci						  const char *name)
387062306a36Sopenharmony_ci{
387162306a36Sopenharmony_ci	struct ibmvscsis_tport *tport =
387262306a36Sopenharmony_ci		container_of(wwn, struct ibmvscsis_tport, tport_wwn);
387362306a36Sopenharmony_ci	u16 tpgt;
387462306a36Sopenharmony_ci	int rc;
387562306a36Sopenharmony_ci
387662306a36Sopenharmony_ci	if (strstr(name, "tpgt_") != name)
387762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
387862306a36Sopenharmony_ci	rc = kstrtou16(name + 5, 0, &tpgt);
387962306a36Sopenharmony_ci	if (rc)
388062306a36Sopenharmony_ci		return ERR_PTR(rc);
388162306a36Sopenharmony_ci	tport->tport_tpgt = tpgt;
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci	tport->releasing = false;
388462306a36Sopenharmony_ci
388562306a36Sopenharmony_ci	rc = core_tpg_register(&tport->tport_wwn, &tport->se_tpg,
388662306a36Sopenharmony_ci			       tport->tport_proto_id);
388762306a36Sopenharmony_ci	if (rc)
388862306a36Sopenharmony_ci		return ERR_PTR(rc);
388962306a36Sopenharmony_ci
389062306a36Sopenharmony_ci	return &tport->se_tpg;
389162306a36Sopenharmony_ci}
389262306a36Sopenharmony_ci
389362306a36Sopenharmony_cistatic void ibmvscsis_drop_tpg(struct se_portal_group *se_tpg)
389462306a36Sopenharmony_ci{
389562306a36Sopenharmony_ci	struct ibmvscsis_tport *tport = container_of(se_tpg,
389662306a36Sopenharmony_ci						     struct ibmvscsis_tport,
389762306a36Sopenharmony_ci						     se_tpg);
389862306a36Sopenharmony_ci
389962306a36Sopenharmony_ci	tport->releasing = true;
390062306a36Sopenharmony_ci	tport->enabled = false;
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci	/*
390362306a36Sopenharmony_ci	 * Release the virtual I_T Nexus for this ibmvscsis TPG
390462306a36Sopenharmony_ci	 */
390562306a36Sopenharmony_ci	ibmvscsis_drop_nexus(tport);
390662306a36Sopenharmony_ci	/*
390762306a36Sopenharmony_ci	 * Deregister the se_tpg from TCM..
390862306a36Sopenharmony_ci	 */
390962306a36Sopenharmony_ci	core_tpg_deregister(se_tpg);
391062306a36Sopenharmony_ci}
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_cistatic ssize_t ibmvscsis_wwn_version_show(struct config_item *item,
391362306a36Sopenharmony_ci					  char *page)
391462306a36Sopenharmony_ci{
391562306a36Sopenharmony_ci	return scnprintf(page, PAGE_SIZE, "%s\n", IBMVSCSIS_VERSION);
391662306a36Sopenharmony_ci}
391762306a36Sopenharmony_ciCONFIGFS_ATTR_RO(ibmvscsis_wwn_, version);
391862306a36Sopenharmony_ci
391962306a36Sopenharmony_cistatic struct configfs_attribute *ibmvscsis_wwn_attrs[] = {
392062306a36Sopenharmony_ci	&ibmvscsis_wwn_attr_version,
392162306a36Sopenharmony_ci	NULL,
392262306a36Sopenharmony_ci};
392362306a36Sopenharmony_ci
392462306a36Sopenharmony_ci
392562306a36Sopenharmony_cistatic int ibmvscsis_enable_tpg(struct se_portal_group *se_tpg, bool enable)
392662306a36Sopenharmony_ci{
392762306a36Sopenharmony_ci	struct ibmvscsis_tport *tport = container_of(se_tpg,
392862306a36Sopenharmony_ci						     struct ibmvscsis_tport,
392962306a36Sopenharmony_ci						     se_tpg);
393062306a36Sopenharmony_ci	struct scsi_info *vscsi = container_of(tport, struct scsi_info, tport);
393162306a36Sopenharmony_ci	long lrc;
393262306a36Sopenharmony_ci
393362306a36Sopenharmony_ci	if (enable) {
393462306a36Sopenharmony_ci		spin_lock_bh(&vscsi->intr_lock);
393562306a36Sopenharmony_ci		tport->enabled = true;
393662306a36Sopenharmony_ci		lrc = ibmvscsis_enable_change_state(vscsi);
393762306a36Sopenharmony_ci		if (lrc)
393862306a36Sopenharmony_ci			dev_err(&vscsi->dev, "enable_change_state failed, rc %ld state %d\n",
393962306a36Sopenharmony_ci				lrc, vscsi->state);
394062306a36Sopenharmony_ci		spin_unlock_bh(&vscsi->intr_lock);
394162306a36Sopenharmony_ci	} else {
394262306a36Sopenharmony_ci		spin_lock_bh(&vscsi->intr_lock);
394362306a36Sopenharmony_ci		tport->enabled = false;
394462306a36Sopenharmony_ci		/* This simulates the server going down */
394562306a36Sopenharmony_ci		ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0);
394662306a36Sopenharmony_ci		spin_unlock_bh(&vscsi->intr_lock);
394762306a36Sopenharmony_ci	}
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ci	return 0;
395062306a36Sopenharmony_ci}
395162306a36Sopenharmony_ci
395262306a36Sopenharmony_cistatic const struct target_core_fabric_ops ibmvscsis_ops = {
395362306a36Sopenharmony_ci	.module				= THIS_MODULE,
395462306a36Sopenharmony_ci	.fabric_name			= "ibmvscsis",
395562306a36Sopenharmony_ci	.max_data_sg_nents		= MAX_TXU / PAGE_SIZE,
395662306a36Sopenharmony_ci	.tpg_get_wwn			= ibmvscsis_get_fabric_wwn,
395762306a36Sopenharmony_ci	.tpg_get_tag			= ibmvscsis_get_tag,
395862306a36Sopenharmony_ci	.tpg_get_default_depth		= ibmvscsis_get_default_depth,
395962306a36Sopenharmony_ci	.tpg_check_demo_mode		= ibmvscsis_check_true,
396062306a36Sopenharmony_ci	.tpg_check_demo_mode_cache	= ibmvscsis_check_true,
396162306a36Sopenharmony_ci	.check_stop_free		= ibmvscsis_check_stop_free,
396262306a36Sopenharmony_ci	.release_cmd			= ibmvscsis_release_cmd,
396362306a36Sopenharmony_ci	.write_pending			= ibmvscsis_write_pending,
396462306a36Sopenharmony_ci	.queue_data_in			= ibmvscsis_queue_data_in,
396562306a36Sopenharmony_ci	.queue_status			= ibmvscsis_queue_status,
396662306a36Sopenharmony_ci	.queue_tm_rsp			= ibmvscsis_queue_tm_rsp,
396762306a36Sopenharmony_ci	.aborted_task			= ibmvscsis_aborted_task,
396862306a36Sopenharmony_ci	/*
396962306a36Sopenharmony_ci	 * Setup function pointers for logic in target_core_fabric_configfs.c
397062306a36Sopenharmony_ci	 */
397162306a36Sopenharmony_ci	.fabric_make_wwn		= ibmvscsis_make_tport,
397262306a36Sopenharmony_ci	.fabric_drop_wwn		= ibmvscsis_drop_tport,
397362306a36Sopenharmony_ci	.fabric_make_tpg		= ibmvscsis_make_tpg,
397462306a36Sopenharmony_ci	.fabric_enable_tpg		= ibmvscsis_enable_tpg,
397562306a36Sopenharmony_ci	.fabric_drop_tpg		= ibmvscsis_drop_tpg,
397662306a36Sopenharmony_ci
397762306a36Sopenharmony_ci	.tfc_wwn_attrs			= ibmvscsis_wwn_attrs,
397862306a36Sopenharmony_ci};
397962306a36Sopenharmony_ci
398062306a36Sopenharmony_cistatic void ibmvscsis_dev_release(struct device *dev) {};
398162306a36Sopenharmony_ci
398262306a36Sopenharmony_cistatic struct device_attribute dev_attr_system_id =
398362306a36Sopenharmony_ci	__ATTR(system_id, S_IRUGO, system_id_show, NULL);
398462306a36Sopenharmony_ci
398562306a36Sopenharmony_cistatic struct device_attribute dev_attr_partition_number =
398662306a36Sopenharmony_ci	__ATTR(partition_number, S_IRUGO, partition_number_show, NULL);
398762306a36Sopenharmony_ci
398862306a36Sopenharmony_cistatic struct device_attribute dev_attr_unit_address =
398962306a36Sopenharmony_ci	__ATTR(unit_address, S_IRUGO, unit_address_show, NULL);
399062306a36Sopenharmony_ci
399162306a36Sopenharmony_cistatic struct attribute *ibmvscsis_dev_attrs[] = {
399262306a36Sopenharmony_ci	&dev_attr_system_id.attr,
399362306a36Sopenharmony_ci	&dev_attr_partition_number.attr,
399462306a36Sopenharmony_ci	&dev_attr_unit_address.attr,
399562306a36Sopenharmony_ci};
399662306a36Sopenharmony_ciATTRIBUTE_GROUPS(ibmvscsis_dev);
399762306a36Sopenharmony_ci
399862306a36Sopenharmony_cistatic struct class ibmvscsis_class = {
399962306a36Sopenharmony_ci	.name		= "ibmvscsis",
400062306a36Sopenharmony_ci	.dev_release	= ibmvscsis_dev_release,
400162306a36Sopenharmony_ci	.dev_groups	= ibmvscsis_dev_groups,
400262306a36Sopenharmony_ci};
400362306a36Sopenharmony_ci
400462306a36Sopenharmony_cistatic const struct vio_device_id ibmvscsis_device_table[] = {
400562306a36Sopenharmony_ci	{ "v-scsi-host", "IBM,v-scsi-host" },
400662306a36Sopenharmony_ci	{ "", "" }
400762306a36Sopenharmony_ci};
400862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(vio, ibmvscsis_device_table);
400962306a36Sopenharmony_ci
401062306a36Sopenharmony_cistatic struct vio_driver ibmvscsis_driver = {
401162306a36Sopenharmony_ci	.name = "ibmvscsis",
401262306a36Sopenharmony_ci	.id_table = ibmvscsis_device_table,
401362306a36Sopenharmony_ci	.probe = ibmvscsis_probe,
401462306a36Sopenharmony_ci	.remove = ibmvscsis_remove,
401562306a36Sopenharmony_ci};
401662306a36Sopenharmony_ci
401762306a36Sopenharmony_ci/*
401862306a36Sopenharmony_ci * ibmvscsis_init() - Kernel Module initialization
401962306a36Sopenharmony_ci *
402062306a36Sopenharmony_ci * Note: vio_register_driver() registers callback functions, and at least one
402162306a36Sopenharmony_ci * of those callback functions calls TCM - Linux IO Target Subsystem, thus
402262306a36Sopenharmony_ci * the SCSI Target template must be registered before vio_register_driver()
402362306a36Sopenharmony_ci * is called.
402462306a36Sopenharmony_ci */
402562306a36Sopenharmony_cistatic int __init ibmvscsis_init(void)
402662306a36Sopenharmony_ci{
402762306a36Sopenharmony_ci	int rc = 0;
402862306a36Sopenharmony_ci
402962306a36Sopenharmony_ci	rc = ibmvscsis_get_system_info();
403062306a36Sopenharmony_ci	if (rc) {
403162306a36Sopenharmony_ci		pr_err("rc %d from get_system_info\n", rc);
403262306a36Sopenharmony_ci		goto out;
403362306a36Sopenharmony_ci	}
403462306a36Sopenharmony_ci
403562306a36Sopenharmony_ci	rc = class_register(&ibmvscsis_class);
403662306a36Sopenharmony_ci	if (rc) {
403762306a36Sopenharmony_ci		pr_err("failed class register\n");
403862306a36Sopenharmony_ci		goto out;
403962306a36Sopenharmony_ci	}
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_ci	rc = target_register_template(&ibmvscsis_ops);
404262306a36Sopenharmony_ci	if (rc) {
404362306a36Sopenharmony_ci		pr_err("rc %d from target_register_template\n", rc);
404462306a36Sopenharmony_ci		goto unregister_class;
404562306a36Sopenharmony_ci	}
404662306a36Sopenharmony_ci
404762306a36Sopenharmony_ci	rc = vio_register_driver(&ibmvscsis_driver);
404862306a36Sopenharmony_ci	if (rc) {
404962306a36Sopenharmony_ci		pr_err("rc %d from vio_register_driver\n", rc);
405062306a36Sopenharmony_ci		goto unregister_target;
405162306a36Sopenharmony_ci	}
405262306a36Sopenharmony_ci
405362306a36Sopenharmony_ci	return 0;
405462306a36Sopenharmony_ci
405562306a36Sopenharmony_ciunregister_target:
405662306a36Sopenharmony_ci	target_unregister_template(&ibmvscsis_ops);
405762306a36Sopenharmony_ciunregister_class:
405862306a36Sopenharmony_ci	class_unregister(&ibmvscsis_class);
405962306a36Sopenharmony_ciout:
406062306a36Sopenharmony_ci	return rc;
406162306a36Sopenharmony_ci}
406262306a36Sopenharmony_ci
406362306a36Sopenharmony_cistatic void __exit ibmvscsis_exit(void)
406462306a36Sopenharmony_ci{
406562306a36Sopenharmony_ci	pr_info("Unregister IBM virtual SCSI host driver\n");
406662306a36Sopenharmony_ci	vio_unregister_driver(&ibmvscsis_driver);
406762306a36Sopenharmony_ci	target_unregister_template(&ibmvscsis_ops);
406862306a36Sopenharmony_ci	class_unregister(&ibmvscsis_class);
406962306a36Sopenharmony_ci}
407062306a36Sopenharmony_ci
407162306a36Sopenharmony_ciMODULE_DESCRIPTION("IBMVSCSIS fabric driver");
407262306a36Sopenharmony_ciMODULE_AUTHOR("Bryant G. Ly and Michael Cyr");
407362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
407462306a36Sopenharmony_ciMODULE_VERSION(IBMVSCSIS_VERSION);
407562306a36Sopenharmony_cimodule_init(ibmvscsis_init);
407662306a36Sopenharmony_cimodule_exit(ibmvscsis_exit);
4077