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