188a678bbSBryant G. Ly /******************************************************************************* 288a678bbSBryant G. Ly * IBM Virtual SCSI Target Driver 388a678bbSBryant G. Ly * Copyright (C) 2003-2005 Dave Boutcher (boutcher@us.ibm.com) IBM Corp. 488a678bbSBryant G. Ly * Santiago Leon (santil@us.ibm.com) IBM Corp. 588a678bbSBryant G. Ly * Linda Xie (lxie@us.ibm.com) IBM Corp. 688a678bbSBryant G. Ly * 788a678bbSBryant G. Ly * Copyright (C) 2005-2011 FUJITA Tomonori <tomof@acm.org> 888a678bbSBryant G. Ly * Copyright (C) 2010 Nicholas A. Bellinger <nab@kernel.org> 988a678bbSBryant G. Ly * 1088a678bbSBryant G. Ly * Authors: Bryant G. Ly <bryantly@linux.vnet.ibm.com> 1188a678bbSBryant G. Ly * Authors: Michael Cyr <mikecyr@linux.vnet.ibm.com> 1288a678bbSBryant G. Ly * 1388a678bbSBryant G. Ly * This program is free software; you can redistribute it and/or modify 1488a678bbSBryant G. Ly * it under the terms of the GNU General Public License as published by 1588a678bbSBryant G. Ly * the Free Software Foundation; either version 2 of the License, or 1688a678bbSBryant G. Ly * (at your option) any later version. 1788a678bbSBryant G. Ly * 1888a678bbSBryant G. Ly * This program is distributed in the hope that it will be useful, 1988a678bbSBryant G. Ly * but WITHOUT ANY WARRANTY; without even the implied warranty of 2088a678bbSBryant G. Ly * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 2188a678bbSBryant G. Ly * GNU General Public License for more details. 2288a678bbSBryant G. Ly * 2388a678bbSBryant G. Ly ****************************************************************************/ 2488a678bbSBryant G. Ly 2588a678bbSBryant G. Ly #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2688a678bbSBryant G. Ly 2788a678bbSBryant G. Ly #include <linux/module.h> 2888a678bbSBryant G. Ly #include <linux/kernel.h> 2988a678bbSBryant G. Ly #include <linux/slab.h> 3088a678bbSBryant G. Ly #include <linux/types.h> 3188a678bbSBryant G. Ly #include <linux/list.h> 3288a678bbSBryant G. Ly #include <linux/string.h> 33e9409b26SBryant G. Ly #include <linux/delay.h> 3488a678bbSBryant G. Ly 3588a678bbSBryant G. Ly #include <target/target_core_base.h> 3688a678bbSBryant G. Ly #include <target/target_core_fabric.h> 3788a678bbSBryant G. Ly 3888a678bbSBryant G. Ly #include <asm/hvcall.h> 3988a678bbSBryant G. Ly #include <asm/vio.h> 4088a678bbSBryant G. Ly 4188a678bbSBryant G. Ly #include <scsi/viosrp.h> 4288a678bbSBryant G. Ly 4388a678bbSBryant G. Ly #include "ibmvscsi_tgt.h" 4488a678bbSBryant G. Ly 4588a678bbSBryant G. Ly #define IBMVSCSIS_VERSION "v0.2" 4688a678bbSBryant G. Ly 4788a678bbSBryant G. Ly #define INITIAL_SRP_LIMIT 800 4888a678bbSBryant G. Ly #define DEFAULT_MAX_SECTORS 256 49387b978cSBryant G. Ly #define MAX_TXU 1024 * 1024 5088a678bbSBryant G. Ly 5188a678bbSBryant G. Ly static uint max_vdma_size = MAX_H_COPY_RDMA; 5288a678bbSBryant G. Ly 5388a678bbSBryant G. Ly static char system_id[SYS_ID_NAME_LEN] = ""; 5488a678bbSBryant G. Ly static char partition_name[PARTITION_NAMELEN] = "UNKNOWN"; 5588a678bbSBryant G. Ly static uint partition_number = -1; 5688a678bbSBryant G. Ly 5788a678bbSBryant G. Ly /* Adapter list and lock to control it */ 5888a678bbSBryant G. Ly static DEFINE_SPINLOCK(ibmvscsis_dev_lock); 5988a678bbSBryant G. Ly static LIST_HEAD(ibmvscsis_dev_list); 6088a678bbSBryant G. Ly 6188a678bbSBryant G. Ly static long ibmvscsis_parse_command(struct scsi_info *vscsi, 6288a678bbSBryant G. Ly struct viosrp_crq *crq); 6388a678bbSBryant G. Ly 6488a678bbSBryant G. Ly static void ibmvscsis_adapter_idle(struct scsi_info *vscsi); 6588a678bbSBryant G. Ly 6688a678bbSBryant G. Ly static void ibmvscsis_determine_resid(struct se_cmd *se_cmd, 6788a678bbSBryant G. Ly struct srp_rsp *rsp) 6888a678bbSBryant G. Ly { 6988a678bbSBryant G. Ly u32 residual_count = se_cmd->residual_count; 7088a678bbSBryant G. Ly 7188a678bbSBryant G. Ly if (!residual_count) 7288a678bbSBryant G. Ly return; 7388a678bbSBryant G. Ly 7488a678bbSBryant G. Ly if (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) { 7588a678bbSBryant G. Ly if (se_cmd->data_direction == DMA_TO_DEVICE) { 7688a678bbSBryant G. Ly /* residual data from an underflow write */ 7788a678bbSBryant G. Ly rsp->flags = SRP_RSP_FLAG_DOUNDER; 7888a678bbSBryant G. Ly rsp->data_out_res_cnt = cpu_to_be32(residual_count); 7988a678bbSBryant G. Ly } else if (se_cmd->data_direction == DMA_FROM_DEVICE) { 8088a678bbSBryant G. Ly /* residual data from an underflow read */ 8188a678bbSBryant G. Ly rsp->flags = SRP_RSP_FLAG_DIUNDER; 8288a678bbSBryant G. Ly rsp->data_in_res_cnt = cpu_to_be32(residual_count); 8388a678bbSBryant G. Ly } 8488a678bbSBryant G. Ly } else if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) { 8588a678bbSBryant G. Ly if (se_cmd->data_direction == DMA_TO_DEVICE) { 8688a678bbSBryant G. Ly /* residual data from an overflow write */ 8788a678bbSBryant G. Ly rsp->flags = SRP_RSP_FLAG_DOOVER; 8888a678bbSBryant G. Ly rsp->data_out_res_cnt = cpu_to_be32(residual_count); 8988a678bbSBryant G. Ly } else if (se_cmd->data_direction == DMA_FROM_DEVICE) { 9088a678bbSBryant G. Ly /* residual data from an overflow read */ 9188a678bbSBryant G. Ly rsp->flags = SRP_RSP_FLAG_DIOVER; 9288a678bbSBryant G. Ly rsp->data_in_res_cnt = cpu_to_be32(residual_count); 9388a678bbSBryant G. Ly } 9488a678bbSBryant G. Ly } 9588a678bbSBryant G. Ly } 9688a678bbSBryant G. Ly 9788a678bbSBryant G. Ly /** 9888a678bbSBryant G. Ly * connection_broken() - Determine if the connection to the client is good 9988a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 10088a678bbSBryant G. Ly * 10188a678bbSBryant G. Ly * This function attempts to send a ping MAD to the client. If the call to 10288a678bbSBryant G. Ly * queue the request returns H_CLOSED then the connection has been broken 10388a678bbSBryant G. Ly * and the function returns TRUE. 10488a678bbSBryant G. Ly * 10588a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 10688a678bbSBryant G. Ly * Interrupt or Process environment 10788a678bbSBryant G. Ly */ 10888a678bbSBryant G. Ly static bool connection_broken(struct scsi_info *vscsi) 10988a678bbSBryant G. Ly { 11088a678bbSBryant G. Ly struct viosrp_crq *crq; 11188a678bbSBryant G. Ly u64 buffer[2] = { 0, 0 }; 11288a678bbSBryant G. Ly long h_return_code; 11388a678bbSBryant G. Ly bool rc = false; 11488a678bbSBryant G. Ly 11588a678bbSBryant G. Ly /* create a PING crq */ 11688a678bbSBryant G. Ly crq = (struct viosrp_crq *)&buffer; 11788a678bbSBryant G. Ly crq->valid = VALID_CMD_RESP_EL; 11888a678bbSBryant G. Ly crq->format = MESSAGE_IN_CRQ; 11988a678bbSBryant G. Ly crq->status = PING; 12088a678bbSBryant G. Ly 12188a678bbSBryant G. Ly h_return_code = h_send_crq(vscsi->dds.unit_id, 12288a678bbSBryant G. Ly cpu_to_be64(buffer[MSG_HI]), 12388a678bbSBryant G. Ly cpu_to_be64(buffer[MSG_LOW])); 12488a678bbSBryant G. Ly 125417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Connection_broken: rc %ld\n", h_return_code); 12688a678bbSBryant G. Ly 12788a678bbSBryant G. Ly if (h_return_code == H_CLOSED) 12888a678bbSBryant G. Ly rc = true; 12988a678bbSBryant G. Ly 13088a678bbSBryant G. Ly return rc; 13188a678bbSBryant G. Ly } 13288a678bbSBryant G. Ly 13388a678bbSBryant G. Ly /** 13488a678bbSBryant G. Ly * ibmvscsis_unregister_command_q() - Helper Function-Unregister Command Queue 13588a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 13688a678bbSBryant G. Ly * 13788a678bbSBryant G. Ly * This function calls h_free_q then frees the interrupt bit etc. 13888a678bbSBryant G. Ly * It must release the lock before doing so because of the time it can take 13988a678bbSBryant G. Ly * for h_free_crq in PHYP 14088a678bbSBryant G. Ly * NOTE: the caller must make sure that state and or flags will prevent 14188a678bbSBryant G. Ly * interrupt handler from scheduling work. 14288a678bbSBryant G. Ly * NOTE: anyone calling this function may need to set the CRQ_CLOSED flag 14388a678bbSBryant G. Ly * we can't do it here, because we don't have the lock 14488a678bbSBryant G. Ly * 14588a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 14688a678bbSBryant G. Ly * Process level 14788a678bbSBryant G. Ly */ 14888a678bbSBryant G. Ly static long ibmvscsis_unregister_command_q(struct scsi_info *vscsi) 14988a678bbSBryant G. Ly { 15088a678bbSBryant G. Ly long qrc; 15188a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 15288a678bbSBryant G. Ly int ticks = 0; 15388a678bbSBryant G. Ly 15488a678bbSBryant G. Ly do { 15588a678bbSBryant G. Ly qrc = h_free_crq(vscsi->dds.unit_id); 15688a678bbSBryant G. Ly switch (qrc) { 15788a678bbSBryant G. Ly case H_SUCCESS: 158464fd641SMichael Cyr spin_lock_bh(&vscsi->intr_lock); 159464fd641SMichael Cyr vscsi->flags &= ~PREP_FOR_SUSPEND_FLAGS; 160464fd641SMichael Cyr spin_unlock_bh(&vscsi->intr_lock); 16188a678bbSBryant G. Ly break; 16288a678bbSBryant G. Ly 16388a678bbSBryant G. Ly case H_HARDWARE: 16488a678bbSBryant G. Ly case H_PARAMETER: 16588a678bbSBryant G. Ly dev_err(&vscsi->dev, "unregister_command_q: error from h_free_crq %ld\n", 16688a678bbSBryant G. Ly qrc); 16788a678bbSBryant G. Ly rc = ERROR; 16888a678bbSBryant G. Ly break; 16988a678bbSBryant G. Ly 17088a678bbSBryant G. Ly case H_BUSY: 17188a678bbSBryant G. Ly case H_LONG_BUSY_ORDER_1_MSEC: 17288a678bbSBryant G. Ly /* msleep not good for small values */ 17388a678bbSBryant G. Ly usleep_range(1000, 2000); 17488a678bbSBryant G. Ly ticks += 1; 17588a678bbSBryant G. Ly break; 17688a678bbSBryant G. Ly case H_LONG_BUSY_ORDER_10_MSEC: 17788a678bbSBryant G. Ly usleep_range(10000, 20000); 17888a678bbSBryant G. Ly ticks += 10; 17988a678bbSBryant G. Ly break; 18088a678bbSBryant G. Ly case H_LONG_BUSY_ORDER_100_MSEC: 18188a678bbSBryant G. Ly msleep(100); 18288a678bbSBryant G. Ly ticks += 100; 18388a678bbSBryant G. Ly break; 18488a678bbSBryant G. Ly case H_LONG_BUSY_ORDER_1_SEC: 18588a678bbSBryant G. Ly ssleep(1); 18688a678bbSBryant G. Ly ticks += 1000; 18788a678bbSBryant G. Ly break; 18888a678bbSBryant G. Ly case H_LONG_BUSY_ORDER_10_SEC: 18988a678bbSBryant G. Ly ssleep(10); 19088a678bbSBryant G. Ly ticks += 10000; 19188a678bbSBryant G. Ly break; 19288a678bbSBryant G. Ly case H_LONG_BUSY_ORDER_100_SEC: 19388a678bbSBryant G. Ly ssleep(100); 19488a678bbSBryant G. Ly ticks += 100000; 19588a678bbSBryant G. Ly break; 19688a678bbSBryant G. Ly default: 19788a678bbSBryant G. Ly dev_err(&vscsi->dev, "unregister_command_q: unknown error %ld from h_free_crq\n", 19888a678bbSBryant G. Ly qrc); 19988a678bbSBryant G. Ly rc = ERROR; 20088a678bbSBryant G. Ly break; 20188a678bbSBryant G. Ly } 20288a678bbSBryant G. Ly 20388a678bbSBryant G. Ly /* 20488a678bbSBryant G. Ly * dont wait more then 300 seconds 20588a678bbSBryant G. Ly * ticks are in milliseconds more or less 20688a678bbSBryant G. Ly */ 20788a678bbSBryant G. Ly if (ticks > 300000 && qrc != H_SUCCESS) { 20888a678bbSBryant G. Ly rc = ERROR; 20988a678bbSBryant G. Ly dev_err(&vscsi->dev, "Excessive wait for h_free_crq\n"); 21088a678bbSBryant G. Ly } 21188a678bbSBryant G. Ly } while (qrc != H_SUCCESS && rc == ADAPT_SUCCESS); 21288a678bbSBryant G. Ly 213417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Freeing CRQ: phyp rc %ld, rc %ld\n", qrc, rc); 21488a678bbSBryant G. Ly 21588a678bbSBryant G. Ly return rc; 21688a678bbSBryant G. Ly } 21788a678bbSBryant G. Ly 21888a678bbSBryant G. Ly /** 21988a678bbSBryant G. Ly * ibmvscsis_delete_client_info() - Helper function to Delete Client Info 22088a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 22188a678bbSBryant G. Ly * @client_closed: True if client closed its queue 22288a678bbSBryant G. Ly * 22388a678bbSBryant G. Ly * Deletes information specific to the client when the client goes away 22488a678bbSBryant G. Ly * 22588a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 22688a678bbSBryant G. Ly * Interrupt or Process 22788a678bbSBryant G. Ly */ 22888a678bbSBryant G. Ly static void ibmvscsis_delete_client_info(struct scsi_info *vscsi, 22988a678bbSBryant G. Ly bool client_closed) 23088a678bbSBryant G. Ly { 23188a678bbSBryant G. Ly vscsi->client_cap = 0; 23288a678bbSBryant G. Ly 23388a678bbSBryant G. Ly /* 23488a678bbSBryant G. Ly * Some things we don't want to clear if we're closing the queue, 23588a678bbSBryant G. Ly * because some clients don't resend the host handshake when they 23688a678bbSBryant G. Ly * get a transport event. 23788a678bbSBryant G. Ly */ 23888a678bbSBryant G. Ly if (client_closed) 23988a678bbSBryant G. Ly vscsi->client_data.os_type = 0; 24088a678bbSBryant G. Ly } 24188a678bbSBryant G. Ly 24288a678bbSBryant G. Ly /** 24388a678bbSBryant G. Ly * ibmvscsis_free_command_q() - Free Command Queue 24488a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 24588a678bbSBryant G. Ly * 24688a678bbSBryant G. Ly * This function calls unregister_command_q, then clears interrupts and 24788a678bbSBryant G. Ly * any pending interrupt acknowledgments associated with the command q. 24888a678bbSBryant G. Ly * It also clears memory if there is no error. 24988a678bbSBryant G. Ly * 25088a678bbSBryant G. Ly * PHYP did not meet the PAPR architecture so that we must give up the 25188a678bbSBryant G. Ly * lock. This causes a timing hole regarding state change. To close the 25288a678bbSBryant G. Ly * hole this routine does accounting on any change that occurred during 25388a678bbSBryant G. Ly * the time the lock is not held. 25488a678bbSBryant G. Ly * NOTE: must give up and then acquire the interrupt lock, the caller must 25588a678bbSBryant G. Ly * make sure that state and or flags will prevent interrupt handler from 25688a678bbSBryant G. Ly * scheduling work. 25788a678bbSBryant G. Ly * 25888a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 25988a678bbSBryant G. Ly * Process level, interrupt lock is held 26088a678bbSBryant G. Ly */ 26188a678bbSBryant G. Ly static long ibmvscsis_free_command_q(struct scsi_info *vscsi) 26288a678bbSBryant G. Ly { 26388a678bbSBryant G. Ly int bytes; 26488a678bbSBryant G. Ly u32 flags_under_lock; 26588a678bbSBryant G. Ly u16 state_under_lock; 26688a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 26788a678bbSBryant G. Ly 26888a678bbSBryant G. Ly if (!(vscsi->flags & CRQ_CLOSED)) { 26988a678bbSBryant G. Ly vio_disable_interrupts(vscsi->dma_dev); 27088a678bbSBryant G. Ly 27188a678bbSBryant G. Ly state_under_lock = vscsi->new_state; 27288a678bbSBryant G. Ly flags_under_lock = vscsi->flags; 27388a678bbSBryant G. Ly vscsi->phyp_acr_state = 0; 27488a678bbSBryant G. Ly vscsi->phyp_acr_flags = 0; 27588a678bbSBryant G. Ly 27688a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 27788a678bbSBryant G. Ly rc = ibmvscsis_unregister_command_q(vscsi); 27888a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 27988a678bbSBryant G. Ly 28088a678bbSBryant G. Ly if (state_under_lock != vscsi->new_state) 28188a678bbSBryant G. Ly vscsi->phyp_acr_state = vscsi->new_state; 28288a678bbSBryant G. Ly 28388a678bbSBryant G. Ly vscsi->phyp_acr_flags = ((~flags_under_lock) & vscsi->flags); 28488a678bbSBryant G. Ly 28588a678bbSBryant G. Ly if (rc == ADAPT_SUCCESS) { 28688a678bbSBryant G. Ly bytes = vscsi->cmd_q.size * PAGE_SIZE; 28788a678bbSBryant G. Ly memset(vscsi->cmd_q.base_addr, 0, bytes); 28888a678bbSBryant G. Ly vscsi->cmd_q.index = 0; 28988a678bbSBryant G. Ly vscsi->flags |= CRQ_CLOSED; 29088a678bbSBryant G. Ly 29188a678bbSBryant G. Ly ibmvscsis_delete_client_info(vscsi, false); 29288a678bbSBryant G. Ly } 29388a678bbSBryant G. Ly 294417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "free_command_q: flags 0x%x, state 0x%hx, acr_flags 0x%x, acr_state 0x%hx\n", 29588a678bbSBryant G. Ly vscsi->flags, vscsi->state, vscsi->phyp_acr_flags, 29688a678bbSBryant G. Ly vscsi->phyp_acr_state); 29788a678bbSBryant G. Ly } 29888a678bbSBryant G. Ly return rc; 29988a678bbSBryant G. Ly } 30088a678bbSBryant G. Ly 30188a678bbSBryant G. Ly /** 30288a678bbSBryant G. Ly * ibmvscsis_cmd_q_dequeue() - Get valid Command element 30388a678bbSBryant G. Ly * @mask: Mask to use in case index wraps 30488a678bbSBryant G. Ly * @current_index: Current index into command queue 30588a678bbSBryant G. Ly * @base_addr: Pointer to start of command queue 30688a678bbSBryant G. Ly * 30788a678bbSBryant G. Ly * Returns a pointer to a valid command element or NULL, if the command 30888a678bbSBryant G. Ly * queue is empty 30988a678bbSBryant G. Ly * 31088a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 31188a678bbSBryant G. Ly * Interrupt environment, interrupt lock held 31288a678bbSBryant G. Ly */ 31388a678bbSBryant G. Ly static struct viosrp_crq *ibmvscsis_cmd_q_dequeue(uint mask, 31488a678bbSBryant G. Ly uint *current_index, 31588a678bbSBryant G. Ly struct viosrp_crq *base_addr) 31688a678bbSBryant G. Ly { 31788a678bbSBryant G. Ly struct viosrp_crq *ptr; 31888a678bbSBryant G. Ly 31988a678bbSBryant G. Ly ptr = base_addr + *current_index; 32088a678bbSBryant G. Ly 32188a678bbSBryant G. Ly if (ptr->valid) { 32288a678bbSBryant G. Ly *current_index = (*current_index + 1) & mask; 32388a678bbSBryant G. Ly dma_rmb(); 32488a678bbSBryant G. Ly } else { 32588a678bbSBryant G. Ly ptr = NULL; 32688a678bbSBryant G. Ly } 32788a678bbSBryant G. Ly 32888a678bbSBryant G. Ly return ptr; 32988a678bbSBryant G. Ly } 33088a678bbSBryant G. Ly 33188a678bbSBryant G. Ly /** 33288a678bbSBryant G. Ly * ibmvscsis_send_init_message() - send initialize message to the client 33388a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 33488a678bbSBryant G. Ly * @format: Which Init Message format to send 33588a678bbSBryant G. Ly * 33688a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 33788a678bbSBryant G. Ly * Interrupt environment interrupt lock held 33888a678bbSBryant G. Ly */ 33988a678bbSBryant G. Ly static long ibmvscsis_send_init_message(struct scsi_info *vscsi, u8 format) 34088a678bbSBryant G. Ly { 34188a678bbSBryant G. Ly struct viosrp_crq *crq; 34288a678bbSBryant G. Ly u64 buffer[2] = { 0, 0 }; 34388a678bbSBryant G. Ly long rc; 34488a678bbSBryant G. Ly 34588a678bbSBryant G. Ly crq = (struct viosrp_crq *)&buffer; 34688a678bbSBryant G. Ly crq->valid = VALID_INIT_MSG; 34788a678bbSBryant G. Ly crq->format = format; 34888a678bbSBryant G. Ly rc = h_send_crq(vscsi->dds.unit_id, cpu_to_be64(buffer[MSG_HI]), 34988a678bbSBryant G. Ly cpu_to_be64(buffer[MSG_LOW])); 35088a678bbSBryant G. Ly 35188a678bbSBryant G. Ly return rc; 35288a678bbSBryant G. Ly } 35388a678bbSBryant G. Ly 35488a678bbSBryant G. Ly /** 35588a678bbSBryant G. Ly * ibmvscsis_check_init_msg() - Check init message valid 35688a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 35788a678bbSBryant G. Ly * @format: Pointer to return format of Init Message, if any. 35888a678bbSBryant G. Ly * Set to UNUSED_FORMAT if no Init Message in queue. 35988a678bbSBryant G. Ly * 36088a678bbSBryant G. Ly * Checks if an initialize message was queued by the initiatior 36188a678bbSBryant G. Ly * after the queue was created and before the interrupt was enabled. 36288a678bbSBryant G. Ly * 36388a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 36488a678bbSBryant G. Ly * Process level only, interrupt lock held 36588a678bbSBryant G. Ly */ 36688a678bbSBryant G. Ly static long ibmvscsis_check_init_msg(struct scsi_info *vscsi, uint *format) 36788a678bbSBryant G. Ly { 36888a678bbSBryant G. Ly struct viosrp_crq *crq; 36988a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 37088a678bbSBryant G. Ly 37188a678bbSBryant G. Ly crq = ibmvscsis_cmd_q_dequeue(vscsi->cmd_q.mask, &vscsi->cmd_q.index, 37288a678bbSBryant G. Ly vscsi->cmd_q.base_addr); 37388a678bbSBryant G. Ly if (!crq) { 37488a678bbSBryant G. Ly *format = (uint)UNUSED_FORMAT; 37588a678bbSBryant G. Ly } else if (crq->valid == VALID_INIT_MSG && crq->format == INIT_MSG) { 37688a678bbSBryant G. Ly *format = (uint)INIT_MSG; 37788a678bbSBryant G. Ly crq->valid = INVALIDATE_CMD_RESP_EL; 37888a678bbSBryant G. Ly dma_rmb(); 37988a678bbSBryant G. Ly 38088a678bbSBryant G. Ly /* 38188a678bbSBryant G. Ly * the caller has ensured no initialize message was 38288a678bbSBryant G. Ly * sent after the queue was 38388a678bbSBryant G. Ly * created so there should be no other message on the queue. 38488a678bbSBryant G. Ly */ 38588a678bbSBryant G. Ly crq = ibmvscsis_cmd_q_dequeue(vscsi->cmd_q.mask, 38688a678bbSBryant G. Ly &vscsi->cmd_q.index, 38788a678bbSBryant G. Ly vscsi->cmd_q.base_addr); 38888a678bbSBryant G. Ly if (crq) { 38988a678bbSBryant G. Ly *format = (uint)(crq->format); 39088a678bbSBryant G. Ly rc = ERROR; 39188a678bbSBryant G. Ly crq->valid = INVALIDATE_CMD_RESP_EL; 39288a678bbSBryant G. Ly dma_rmb(); 39388a678bbSBryant G. Ly } 39488a678bbSBryant G. Ly } else { 39588a678bbSBryant G. Ly *format = (uint)(crq->format); 39688a678bbSBryant G. Ly rc = ERROR; 39788a678bbSBryant G. Ly crq->valid = INVALIDATE_CMD_RESP_EL; 39888a678bbSBryant G. Ly dma_rmb(); 39988a678bbSBryant G. Ly } 40088a678bbSBryant G. Ly 40188a678bbSBryant G. Ly return rc; 40288a678bbSBryant G. Ly } 40388a678bbSBryant G. Ly 40488a678bbSBryant G. Ly /** 40588a678bbSBryant G. Ly * ibmvscsis_disconnect() - Helper function to disconnect 40688a678bbSBryant G. Ly * @work: Pointer to work_struct, gives access to our adapter structure 40788a678bbSBryant G. Ly * 40888a678bbSBryant G. Ly * An error has occurred or the driver received a Transport event, 40988a678bbSBryant G. Ly * and the driver is requesting that the command queue be de-registered 41088a678bbSBryant G. Ly * in a safe manner. If there is no outstanding I/O then we can stop the 41188a678bbSBryant G. Ly * queue. If we are restarting the queue it will be reflected in the 41288a678bbSBryant G. Ly * the state of the adapter. 41388a678bbSBryant G. Ly * 41488a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 41588a678bbSBryant G. Ly * Process environment 41688a678bbSBryant G. Ly */ 41788a678bbSBryant G. Ly static void ibmvscsis_disconnect(struct work_struct *work) 41888a678bbSBryant G. Ly { 41988a678bbSBryant G. Ly struct scsi_info *vscsi = container_of(work, struct scsi_info, 42088a678bbSBryant G. Ly proc_work); 42188a678bbSBryant G. Ly u16 new_state; 42288a678bbSBryant G. Ly bool wait_idle = false; 42388a678bbSBryant G. Ly 42488a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 42588a678bbSBryant G. Ly new_state = vscsi->new_state; 42688a678bbSBryant G. Ly vscsi->new_state = 0; 42788a678bbSBryant G. Ly 428464fd641SMichael Cyr vscsi->flags |= DISCONNECT_SCHEDULED; 429464fd641SMichael Cyr vscsi->flags &= ~SCHEDULE_DISCONNECT; 430464fd641SMichael Cyr 431417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "disconnect: flags 0x%x, state 0x%hx\n", 432417dff6cSBryant G. Ly vscsi->flags, vscsi->state); 43388a678bbSBryant G. Ly 43488a678bbSBryant G. Ly /* 43588a678bbSBryant G. Ly * check which state we are in and see if we 43688a678bbSBryant G. Ly * should transitition to the new state 43788a678bbSBryant G. Ly */ 43888a678bbSBryant G. Ly switch (vscsi->state) { 43988a678bbSBryant G. Ly /* Should never be called while in this state. */ 44088a678bbSBryant G. Ly case NO_QUEUE: 44188a678bbSBryant G. Ly /* 44288a678bbSBryant G. Ly * Can never transition from this state; 44388a678bbSBryant G. Ly * igonore errors and logout. 44488a678bbSBryant G. Ly */ 44588a678bbSBryant G. Ly case UNCONFIGURING: 44688a678bbSBryant G. Ly break; 44788a678bbSBryant G. Ly 44888a678bbSBryant G. Ly /* can transition from this state to UNCONFIGURING */ 44988a678bbSBryant G. Ly case ERR_DISCONNECT: 45088a678bbSBryant G. Ly if (new_state == UNCONFIGURING) 45188a678bbSBryant G. Ly vscsi->state = new_state; 45288a678bbSBryant G. Ly break; 45388a678bbSBryant G. Ly 45488a678bbSBryant G. Ly /* 45588a678bbSBryant G. Ly * Can transition from this state to to unconfiguring 45688a678bbSBryant G. Ly * or err disconnect. 45788a678bbSBryant G. Ly */ 45888a678bbSBryant G. Ly case ERR_DISCONNECT_RECONNECT: 45988a678bbSBryant G. Ly switch (new_state) { 46088a678bbSBryant G. Ly case UNCONFIGURING: 46188a678bbSBryant G. Ly case ERR_DISCONNECT: 46288a678bbSBryant G. Ly vscsi->state = new_state; 46388a678bbSBryant G. Ly break; 46488a678bbSBryant G. Ly 46588a678bbSBryant G. Ly case WAIT_IDLE: 46688a678bbSBryant G. Ly break; 46788a678bbSBryant G. Ly default: 46888a678bbSBryant G. Ly break; 46988a678bbSBryant G. Ly } 47088a678bbSBryant G. Ly break; 47188a678bbSBryant G. Ly 47288a678bbSBryant G. Ly /* can transition from this state to UNCONFIGURING */ 47388a678bbSBryant G. Ly case ERR_DISCONNECTED: 47488a678bbSBryant G. Ly if (new_state == UNCONFIGURING) 47588a678bbSBryant G. Ly vscsi->state = new_state; 47688a678bbSBryant G. Ly break; 47788a678bbSBryant G. Ly 47888a678bbSBryant G. Ly case WAIT_ENABLED: 47988a678bbSBryant G. Ly switch (new_state) { 4808bf11557SMichael Cyr case UNCONFIGURING: 4818bf11557SMichael Cyr vscsi->state = new_state; 4828bf11557SMichael Cyr vscsi->flags |= RESPONSE_Q_DOWN; 4838bf11557SMichael Cyr vscsi->flags &= ~(SCHEDULE_DISCONNECT | 4848bf11557SMichael Cyr DISCONNECT_SCHEDULED); 4858bf11557SMichael Cyr dma_rmb(); 4868bf11557SMichael Cyr if (vscsi->flags & CFG_SLEEPING) { 4878bf11557SMichael Cyr vscsi->flags &= ~CFG_SLEEPING; 4888bf11557SMichael Cyr complete(&vscsi->unconfig); 4898bf11557SMichael Cyr } 4908bf11557SMichael Cyr break; 4918bf11557SMichael Cyr 49288a678bbSBryant G. Ly /* should never happen */ 493c9b3379fSMichael Cyr case ERR_DISCONNECT: 494c9b3379fSMichael Cyr case ERR_DISCONNECT_RECONNECT: 49588a678bbSBryant G. Ly case WAIT_IDLE: 49688a678bbSBryant G. Ly dev_err(&vscsi->dev, "disconnect: invalid state %d for WAIT_IDLE\n", 49788a678bbSBryant G. Ly vscsi->state); 49888a678bbSBryant G. Ly break; 49988a678bbSBryant G. Ly } 50088a678bbSBryant G. Ly break; 50188a678bbSBryant G. Ly 50288a678bbSBryant G. Ly case WAIT_IDLE: 50388a678bbSBryant G. Ly switch (new_state) { 5048bf11557SMichael Cyr case UNCONFIGURING: 5058bf11557SMichael Cyr vscsi->flags |= RESPONSE_Q_DOWN; 5068bf11557SMichael Cyr vscsi->state = new_state; 5078bf11557SMichael Cyr vscsi->flags &= ~(SCHEDULE_DISCONNECT | 5088bf11557SMichael Cyr DISCONNECT_SCHEDULED); 5098bf11557SMichael Cyr ibmvscsis_free_command_q(vscsi); 5108bf11557SMichael Cyr break; 51188a678bbSBryant G. Ly case ERR_DISCONNECT: 51288a678bbSBryant G. Ly case ERR_DISCONNECT_RECONNECT: 51388a678bbSBryant G. Ly vscsi->state = new_state; 51488a678bbSBryant G. Ly break; 51588a678bbSBryant G. Ly } 51688a678bbSBryant G. Ly break; 51788a678bbSBryant G. Ly 51888a678bbSBryant G. Ly /* 51988a678bbSBryant G. Ly * Initiator has not done a successful srp login 52088a678bbSBryant G. Ly * or has done a successful srp logout ( adapter was not 52188a678bbSBryant G. Ly * busy). In the first case there can be responses queued 52288a678bbSBryant G. Ly * waiting for space on the initiators response queue (MAD) 52388a678bbSBryant G. Ly * The second case the adapter is idle. Assume the worse case, 52488a678bbSBryant G. Ly * i.e. the second case. 52588a678bbSBryant G. Ly */ 52688a678bbSBryant G. Ly case WAIT_CONNECTION: 52788a678bbSBryant G. Ly case CONNECTED: 52888a678bbSBryant G. Ly case SRP_PROCESSING: 52988a678bbSBryant G. Ly wait_idle = true; 53088a678bbSBryant G. Ly vscsi->state = new_state; 53188a678bbSBryant G. Ly break; 53288a678bbSBryant G. Ly 53388a678bbSBryant G. Ly /* can transition from this state to UNCONFIGURING */ 53488a678bbSBryant G. Ly case UNDEFINED: 53588a678bbSBryant G. Ly if (new_state == UNCONFIGURING) 53688a678bbSBryant G. Ly vscsi->state = new_state; 53788a678bbSBryant G. Ly break; 53888a678bbSBryant G. Ly default: 53988a678bbSBryant G. Ly break; 54088a678bbSBryant G. Ly } 54188a678bbSBryant G. Ly 54288a678bbSBryant G. Ly if (wait_idle) { 543417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "disconnect start wait, active %d, sched %d\n", 54488a678bbSBryant G. Ly (int)list_empty(&vscsi->active_q), 54588a678bbSBryant G. Ly (int)list_empty(&vscsi->schedule_q)); 54688a678bbSBryant G. Ly if (!list_empty(&vscsi->active_q) || 54788a678bbSBryant G. Ly !list_empty(&vscsi->schedule_q)) { 54888a678bbSBryant G. Ly vscsi->flags |= WAIT_FOR_IDLE; 549417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "disconnect flags 0x%x\n", 550417dff6cSBryant G. Ly vscsi->flags); 55188a678bbSBryant G. Ly /* 55288a678bbSBryant G. Ly * This routine is can not be called with the interrupt 55388a678bbSBryant G. Ly * lock held. 55488a678bbSBryant G. Ly */ 55588a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 55688a678bbSBryant G. Ly wait_for_completion(&vscsi->wait_idle); 55788a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 55888a678bbSBryant G. Ly } 559417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "disconnect stop wait\n"); 56088a678bbSBryant G. Ly 56188a678bbSBryant G. Ly ibmvscsis_adapter_idle(vscsi); 56288a678bbSBryant G. Ly } 56388a678bbSBryant G. Ly 56488a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 56588a678bbSBryant G. Ly } 56688a678bbSBryant G. Ly 56788a678bbSBryant G. Ly /** 56888a678bbSBryant G. Ly * ibmvscsis_post_disconnect() - Schedule the disconnect 56988a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 57088a678bbSBryant G. Ly * @new_state: State to move to after disconnecting 57188a678bbSBryant G. Ly * @flag_bits: Flags to turn on in adapter structure 57288a678bbSBryant G. Ly * 57388a678bbSBryant G. Ly * If it's already been scheduled, then see if we need to "upgrade" 57488a678bbSBryant G. Ly * the new state (if the one passed in is more "severe" than the 57588a678bbSBryant G. Ly * previous one). 57688a678bbSBryant G. Ly * 57788a678bbSBryant G. Ly * PRECONDITION: 57888a678bbSBryant G. Ly * interrupt lock is held 57988a678bbSBryant G. Ly */ 58088a678bbSBryant G. Ly static void ibmvscsis_post_disconnect(struct scsi_info *vscsi, uint new_state, 58188a678bbSBryant G. Ly uint flag_bits) 58288a678bbSBryant G. Ly { 58388a678bbSBryant G. Ly uint state; 58488a678bbSBryant G. Ly 58588a678bbSBryant G. Ly /* check the validity of the new state */ 58688a678bbSBryant G. Ly switch (new_state) { 58788a678bbSBryant G. Ly case UNCONFIGURING: 58888a678bbSBryant G. Ly case ERR_DISCONNECT: 58988a678bbSBryant G. Ly case ERR_DISCONNECT_RECONNECT: 59088a678bbSBryant G. Ly case WAIT_IDLE: 59188a678bbSBryant G. Ly break; 59288a678bbSBryant G. Ly 59388a678bbSBryant G. Ly default: 59488a678bbSBryant G. Ly dev_err(&vscsi->dev, "post_disconnect: Invalid new state %d\n", 59588a678bbSBryant G. Ly new_state); 59688a678bbSBryant G. Ly return; 59788a678bbSBryant G. Ly } 59888a678bbSBryant G. Ly 59988a678bbSBryant G. Ly vscsi->flags |= flag_bits; 60088a678bbSBryant G. Ly 601417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "post_disconnect: new_state 0x%x, flag_bits 0x%x, vscsi->flags 0x%x, state %hx\n", 60288a678bbSBryant G. Ly new_state, flag_bits, vscsi->flags, vscsi->state); 60388a678bbSBryant G. Ly 60488a678bbSBryant G. Ly if (!(vscsi->flags & (DISCONNECT_SCHEDULED | SCHEDULE_DISCONNECT))) { 60588a678bbSBryant G. Ly vscsi->flags |= SCHEDULE_DISCONNECT; 60688a678bbSBryant G. Ly vscsi->new_state = new_state; 60788a678bbSBryant G. Ly 60888a678bbSBryant G. Ly INIT_WORK(&vscsi->proc_work, ibmvscsis_disconnect); 60988a678bbSBryant G. Ly (void)queue_work(vscsi->work_q, &vscsi->proc_work); 61088a678bbSBryant G. Ly } else { 61188a678bbSBryant G. Ly if (vscsi->new_state) 61288a678bbSBryant G. Ly state = vscsi->new_state; 61388a678bbSBryant G. Ly else 61488a678bbSBryant G. Ly state = vscsi->state; 61588a678bbSBryant G. Ly 61688a678bbSBryant G. Ly switch (state) { 61788a678bbSBryant G. Ly case NO_QUEUE: 61888a678bbSBryant G. Ly case UNCONFIGURING: 61988a678bbSBryant G. Ly break; 62088a678bbSBryant G. Ly 62188a678bbSBryant G. Ly case ERR_DISCONNECTED: 62288a678bbSBryant G. Ly case ERR_DISCONNECT: 62388a678bbSBryant G. Ly case UNDEFINED: 62488a678bbSBryant G. Ly if (new_state == UNCONFIGURING) 62588a678bbSBryant G. Ly vscsi->new_state = new_state; 62688a678bbSBryant G. Ly break; 62788a678bbSBryant G. Ly 62888a678bbSBryant G. Ly case ERR_DISCONNECT_RECONNECT: 62988a678bbSBryant G. Ly switch (new_state) { 63088a678bbSBryant G. Ly case UNCONFIGURING: 63188a678bbSBryant G. Ly case ERR_DISCONNECT: 63288a678bbSBryant G. Ly vscsi->new_state = new_state; 63388a678bbSBryant G. Ly break; 63488a678bbSBryant G. Ly default: 63588a678bbSBryant G. Ly break; 63688a678bbSBryant G. Ly } 63788a678bbSBryant G. Ly break; 63888a678bbSBryant G. Ly 63988a678bbSBryant G. Ly case WAIT_ENABLED: 64088a678bbSBryant G. Ly case WAIT_IDLE: 64188a678bbSBryant G. Ly case WAIT_CONNECTION: 64288a678bbSBryant G. Ly case CONNECTED: 64388a678bbSBryant G. Ly case SRP_PROCESSING: 64488a678bbSBryant G. Ly vscsi->new_state = new_state; 64588a678bbSBryant G. Ly break; 64688a678bbSBryant G. Ly 64788a678bbSBryant G. Ly default: 64888a678bbSBryant G. Ly break; 64988a678bbSBryant G. Ly } 65088a678bbSBryant G. Ly } 65188a678bbSBryant G. Ly 652417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Leaving post_disconnect: flags 0x%x, new_state 0x%x\n", 65388a678bbSBryant G. Ly vscsi->flags, vscsi->new_state); 65488a678bbSBryant G. Ly } 65588a678bbSBryant G. Ly 65688a678bbSBryant G. Ly /** 65779fac9c9SMichael Cyr * ibmvscsis_handle_init_compl_msg() - Respond to an Init Complete Message 65879fac9c9SMichael Cyr * @vscsi: Pointer to our adapter structure 65979fac9c9SMichael Cyr * 66079fac9c9SMichael Cyr * Must be called with interrupt lock held. 66179fac9c9SMichael Cyr */ 66279fac9c9SMichael Cyr static long ibmvscsis_handle_init_compl_msg(struct scsi_info *vscsi) 66379fac9c9SMichael Cyr { 66479fac9c9SMichael Cyr long rc = ADAPT_SUCCESS; 66579fac9c9SMichael Cyr 66679fac9c9SMichael Cyr switch (vscsi->state) { 66779fac9c9SMichael Cyr case NO_QUEUE: 66879fac9c9SMichael Cyr case ERR_DISCONNECT: 66979fac9c9SMichael Cyr case ERR_DISCONNECT_RECONNECT: 67079fac9c9SMichael Cyr case ERR_DISCONNECTED: 67179fac9c9SMichael Cyr case UNCONFIGURING: 67279fac9c9SMichael Cyr case UNDEFINED: 67379fac9c9SMichael Cyr rc = ERROR; 67479fac9c9SMichael Cyr break; 67579fac9c9SMichael Cyr 67679fac9c9SMichael Cyr case WAIT_CONNECTION: 67779fac9c9SMichael Cyr vscsi->state = CONNECTED; 67879fac9c9SMichael Cyr break; 67979fac9c9SMichael Cyr 68079fac9c9SMichael Cyr case WAIT_IDLE: 68179fac9c9SMichael Cyr case SRP_PROCESSING: 68279fac9c9SMichael Cyr case CONNECTED: 68379fac9c9SMichael Cyr case WAIT_ENABLED: 68479fac9c9SMichael Cyr default: 68579fac9c9SMichael Cyr rc = ERROR; 68679fac9c9SMichael Cyr dev_err(&vscsi->dev, "init_msg: invalid state %d to get init compl msg\n", 68779fac9c9SMichael Cyr vscsi->state); 68879fac9c9SMichael Cyr ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 68979fac9c9SMichael Cyr break; 69079fac9c9SMichael Cyr } 69179fac9c9SMichael Cyr 69279fac9c9SMichael Cyr return rc; 69379fac9c9SMichael Cyr } 69479fac9c9SMichael Cyr 69579fac9c9SMichael Cyr /** 69679fac9c9SMichael Cyr * ibmvscsis_handle_init_msg() - Respond to an Init Message 69779fac9c9SMichael Cyr * @vscsi: Pointer to our adapter structure 69879fac9c9SMichael Cyr * 69979fac9c9SMichael Cyr * Must be called with interrupt lock held. 70079fac9c9SMichael Cyr */ 70179fac9c9SMichael Cyr static long ibmvscsis_handle_init_msg(struct scsi_info *vscsi) 70279fac9c9SMichael Cyr { 70379fac9c9SMichael Cyr long rc = ADAPT_SUCCESS; 70479fac9c9SMichael Cyr 70579fac9c9SMichael Cyr switch (vscsi->state) { 70679fac9c9SMichael Cyr case WAIT_CONNECTION: 70779fac9c9SMichael Cyr rc = ibmvscsis_send_init_message(vscsi, INIT_COMPLETE_MSG); 70879fac9c9SMichael Cyr switch (rc) { 70979fac9c9SMichael Cyr case H_SUCCESS: 71079fac9c9SMichael Cyr vscsi->state = CONNECTED; 71179fac9c9SMichael Cyr break; 71279fac9c9SMichael Cyr 71379fac9c9SMichael Cyr case H_PARAMETER: 71479fac9c9SMichael Cyr dev_err(&vscsi->dev, "init_msg: failed to send, rc %ld\n", 71579fac9c9SMichael Cyr rc); 71679fac9c9SMichael Cyr ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0); 71779fac9c9SMichael Cyr break; 71879fac9c9SMichael Cyr 71979fac9c9SMichael Cyr case H_DROPPED: 72079fac9c9SMichael Cyr dev_err(&vscsi->dev, "init_msg: failed to send, rc %ld\n", 72179fac9c9SMichael Cyr rc); 72279fac9c9SMichael Cyr rc = ERROR; 72379fac9c9SMichael Cyr ibmvscsis_post_disconnect(vscsi, 72479fac9c9SMichael Cyr ERR_DISCONNECT_RECONNECT, 0); 72579fac9c9SMichael Cyr break; 72679fac9c9SMichael Cyr 72779fac9c9SMichael Cyr case H_CLOSED: 728417dff6cSBryant G. Ly dev_warn(&vscsi->dev, "init_msg: failed to send, rc %ld\n", 729417dff6cSBryant G. Ly rc); 73079fac9c9SMichael Cyr rc = 0; 73179fac9c9SMichael Cyr break; 73279fac9c9SMichael Cyr } 73379fac9c9SMichael Cyr break; 73479fac9c9SMichael Cyr 73579fac9c9SMichael Cyr case UNDEFINED: 73679fac9c9SMichael Cyr rc = ERROR; 73779fac9c9SMichael Cyr break; 73879fac9c9SMichael Cyr 73979fac9c9SMichael Cyr case UNCONFIGURING: 74079fac9c9SMichael Cyr break; 74179fac9c9SMichael Cyr 742c9b3379fSMichael Cyr case WAIT_ENABLED: 74379fac9c9SMichael Cyr case CONNECTED: 74479fac9c9SMichael Cyr case SRP_PROCESSING: 74579fac9c9SMichael Cyr case WAIT_IDLE: 74679fac9c9SMichael Cyr case NO_QUEUE: 74779fac9c9SMichael Cyr case ERR_DISCONNECT: 74879fac9c9SMichael Cyr case ERR_DISCONNECT_RECONNECT: 74979fac9c9SMichael Cyr case ERR_DISCONNECTED: 75079fac9c9SMichael Cyr default: 75179fac9c9SMichael Cyr rc = ERROR; 75279fac9c9SMichael Cyr dev_err(&vscsi->dev, "init_msg: invalid state %d to get init msg\n", 75379fac9c9SMichael Cyr vscsi->state); 75479fac9c9SMichael Cyr ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 75579fac9c9SMichael Cyr break; 75679fac9c9SMichael Cyr } 75779fac9c9SMichael Cyr 75879fac9c9SMichael Cyr return rc; 75979fac9c9SMichael Cyr } 76079fac9c9SMichael Cyr 76179fac9c9SMichael Cyr /** 76279fac9c9SMichael Cyr * ibmvscsis_init_msg() - Respond to an init message 76379fac9c9SMichael Cyr * @vscsi: Pointer to our adapter structure 76479fac9c9SMichael Cyr * @crq: Pointer to CRQ element containing the Init Message 76579fac9c9SMichael Cyr * 76679fac9c9SMichael Cyr * EXECUTION ENVIRONMENT: 76779fac9c9SMichael Cyr * Interrupt, interrupt lock held 76879fac9c9SMichael Cyr */ 76979fac9c9SMichael Cyr static long ibmvscsis_init_msg(struct scsi_info *vscsi, struct viosrp_crq *crq) 77079fac9c9SMichael Cyr { 77179fac9c9SMichael Cyr long rc = ADAPT_SUCCESS; 77279fac9c9SMichael Cyr 773417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "init_msg: state 0x%hx\n", vscsi->state); 77479fac9c9SMichael Cyr 77579fac9c9SMichael Cyr rc = h_vioctl(vscsi->dds.unit_id, H_GET_PARTNER_INFO, 77679fac9c9SMichael Cyr (u64)vscsi->map_ioba | ((u64)PAGE_SIZE << 32), 0, 0, 0, 77779fac9c9SMichael Cyr 0); 77879fac9c9SMichael Cyr if (rc == H_SUCCESS) { 77979fac9c9SMichael Cyr vscsi->client_data.partition_number = 78079fac9c9SMichael Cyr be64_to_cpu(*(u64 *)vscsi->map_buf); 781417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "init_msg, part num %d\n", 78279fac9c9SMichael Cyr vscsi->client_data.partition_number); 78379fac9c9SMichael Cyr } else { 784417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "init_msg h_vioctl rc %ld\n", rc); 78579fac9c9SMichael Cyr rc = ADAPT_SUCCESS; 78679fac9c9SMichael Cyr } 78779fac9c9SMichael Cyr 78879fac9c9SMichael Cyr if (crq->format == INIT_MSG) { 78979fac9c9SMichael Cyr rc = ibmvscsis_handle_init_msg(vscsi); 79079fac9c9SMichael Cyr } else if (crq->format == INIT_COMPLETE_MSG) { 79179fac9c9SMichael Cyr rc = ibmvscsis_handle_init_compl_msg(vscsi); 79279fac9c9SMichael Cyr } else { 79379fac9c9SMichael Cyr rc = ERROR; 79479fac9c9SMichael Cyr dev_err(&vscsi->dev, "init_msg: invalid format %d\n", 79579fac9c9SMichael Cyr (uint)crq->format); 79679fac9c9SMichael Cyr ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 79779fac9c9SMichael Cyr } 79879fac9c9SMichael Cyr 79979fac9c9SMichael Cyr return rc; 80079fac9c9SMichael Cyr } 80179fac9c9SMichael Cyr 80279fac9c9SMichael Cyr /** 80379fac9c9SMichael Cyr * ibmvscsis_establish_new_q() - Establish new CRQ queue 80479fac9c9SMichael Cyr * @vscsi: Pointer to our adapter structure 80579fac9c9SMichael Cyr * 80679fac9c9SMichael Cyr * Must be called with interrupt lock held. 80779fac9c9SMichael Cyr */ 808c9b3379fSMichael Cyr static long ibmvscsis_establish_new_q(struct scsi_info *vscsi) 80979fac9c9SMichael Cyr { 81079fac9c9SMichael Cyr long rc = ADAPT_SUCCESS; 81179fac9c9SMichael Cyr uint format; 81279fac9c9SMichael Cyr 813464fd641SMichael Cyr rc = h_vioctl(vscsi->dds.unit_id, H_ENABLE_PREPARE_FOR_SUSPEND, 30000, 814464fd641SMichael Cyr 0, 0, 0, 0); 815464fd641SMichael Cyr if (rc == H_SUCCESS) 816464fd641SMichael Cyr vscsi->flags |= PREP_FOR_SUSPEND_ENABLED; 817464fd641SMichael Cyr else if (rc != H_NOT_FOUND) 818417dff6cSBryant G. Ly dev_err(&vscsi->dev, "Error from Enable Prepare for Suspend: %ld\n", 819417dff6cSBryant G. Ly rc); 820464fd641SMichael Cyr 82179fac9c9SMichael Cyr vscsi->flags &= PRESERVE_FLAG_FIELDS; 82279fac9c9SMichael Cyr vscsi->rsp_q_timer.timer_pops = 0; 82379fac9c9SMichael Cyr vscsi->debit = 0; 82479fac9c9SMichael Cyr vscsi->credit = 0; 82579fac9c9SMichael Cyr 82679fac9c9SMichael Cyr rc = vio_enable_interrupts(vscsi->dma_dev); 82779fac9c9SMichael Cyr if (rc) { 828417dff6cSBryant G. Ly dev_warn(&vscsi->dev, "establish_new_q: failed to enable interrupts, rc %ld\n", 82979fac9c9SMichael Cyr rc); 83079fac9c9SMichael Cyr return rc; 83179fac9c9SMichael Cyr } 83279fac9c9SMichael Cyr 83379fac9c9SMichael Cyr rc = ibmvscsis_check_init_msg(vscsi, &format); 83479fac9c9SMichael Cyr if (rc) { 835c9b3379fSMichael Cyr dev_err(&vscsi->dev, "establish_new_q: check_init_msg failed, rc %ld\n", 83679fac9c9SMichael Cyr rc); 83779fac9c9SMichael Cyr return rc; 83879fac9c9SMichael Cyr } 83979fac9c9SMichael Cyr 840c9b3379fSMichael Cyr if (format == UNUSED_FORMAT) { 84179fac9c9SMichael Cyr rc = ibmvscsis_send_init_message(vscsi, INIT_MSG); 84279fac9c9SMichael Cyr switch (rc) { 84379fac9c9SMichael Cyr case H_SUCCESS: 84479fac9c9SMichael Cyr case H_DROPPED: 84579fac9c9SMichael Cyr case H_CLOSED: 84679fac9c9SMichael Cyr rc = ADAPT_SUCCESS; 84779fac9c9SMichael Cyr break; 84879fac9c9SMichael Cyr 84979fac9c9SMichael Cyr case H_PARAMETER: 85079fac9c9SMichael Cyr case H_HARDWARE: 85179fac9c9SMichael Cyr break; 85279fac9c9SMichael Cyr 85379fac9c9SMichael Cyr default: 85479fac9c9SMichael Cyr vscsi->state = UNDEFINED; 85579fac9c9SMichael Cyr rc = H_HARDWARE; 85679fac9c9SMichael Cyr break; 85779fac9c9SMichael Cyr } 858c9b3379fSMichael Cyr } else if (format == INIT_MSG) { 859c9b3379fSMichael Cyr rc = ibmvscsis_handle_init_msg(vscsi); 86079fac9c9SMichael Cyr } 86179fac9c9SMichael Cyr 86279fac9c9SMichael Cyr return rc; 86379fac9c9SMichael Cyr } 86479fac9c9SMichael Cyr 86579fac9c9SMichael Cyr /** 86679fac9c9SMichael Cyr * ibmvscsis_reset_queue() - Reset CRQ Queue 86779fac9c9SMichael Cyr * @vscsi: Pointer to our adapter structure 86879fac9c9SMichael Cyr * 86979fac9c9SMichael Cyr * This function calls h_free_q and then calls h_reg_q and does all 87079fac9c9SMichael Cyr * of the bookkeeping to get us back to where we can communicate. 87179fac9c9SMichael Cyr * 87279fac9c9SMichael Cyr * Actually, we don't always call h_free_crq. A problem was discovered 87379fac9c9SMichael Cyr * where one partition would close and reopen his queue, which would 87479fac9c9SMichael Cyr * cause his partner to get a transport event, which would cause him to 87579fac9c9SMichael Cyr * close and reopen his queue, which would cause the original partition 87679fac9c9SMichael Cyr * to get a transport event, etc., etc. To prevent this, we don't 87779fac9c9SMichael Cyr * actually close our queue if the client initiated the reset, (i.e. 87879fac9c9SMichael Cyr * either we got a transport event or we have detected that the client's 87979fac9c9SMichael Cyr * queue is gone) 88079fac9c9SMichael Cyr * 88179fac9c9SMichael Cyr * EXECUTION ENVIRONMENT: 88279fac9c9SMichael Cyr * Process environment, called with interrupt lock held 88379fac9c9SMichael Cyr */ 884c9b3379fSMichael Cyr static void ibmvscsis_reset_queue(struct scsi_info *vscsi) 88579fac9c9SMichael Cyr { 88679fac9c9SMichael Cyr int bytes; 88779fac9c9SMichael Cyr long rc = ADAPT_SUCCESS; 88879fac9c9SMichael Cyr 889417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "reset_queue: flags 0x%x\n", vscsi->flags); 89079fac9c9SMichael Cyr 89179fac9c9SMichael Cyr /* don't reset, the client did it for us */ 89279fac9c9SMichael Cyr if (vscsi->flags & (CLIENT_FAILED | TRANS_EVENT)) { 89379fac9c9SMichael Cyr vscsi->flags &= PRESERVE_FLAG_FIELDS; 89479fac9c9SMichael Cyr vscsi->rsp_q_timer.timer_pops = 0; 89579fac9c9SMichael Cyr vscsi->debit = 0; 89679fac9c9SMichael Cyr vscsi->credit = 0; 897c9b3379fSMichael Cyr vscsi->state = WAIT_CONNECTION; 89879fac9c9SMichael Cyr vio_enable_interrupts(vscsi->dma_dev); 89979fac9c9SMichael Cyr } else { 90079fac9c9SMichael Cyr rc = ibmvscsis_free_command_q(vscsi); 90179fac9c9SMichael Cyr if (rc == ADAPT_SUCCESS) { 902c9b3379fSMichael Cyr vscsi->state = WAIT_CONNECTION; 90379fac9c9SMichael Cyr 90479fac9c9SMichael Cyr bytes = vscsi->cmd_q.size * PAGE_SIZE; 90579fac9c9SMichael Cyr rc = h_reg_crq(vscsi->dds.unit_id, 90679fac9c9SMichael Cyr vscsi->cmd_q.crq_token, bytes); 90779fac9c9SMichael Cyr if (rc == H_CLOSED || rc == H_SUCCESS) { 908c9b3379fSMichael Cyr rc = ibmvscsis_establish_new_q(vscsi); 90979fac9c9SMichael Cyr } 91079fac9c9SMichael Cyr 91179fac9c9SMichael Cyr if (rc != ADAPT_SUCCESS) { 912417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "reset_queue: reg_crq rc %ld\n", 913417dff6cSBryant G. Ly rc); 91479fac9c9SMichael Cyr 91579fac9c9SMichael Cyr vscsi->state = ERR_DISCONNECTED; 91679fac9c9SMichael Cyr vscsi->flags |= RESPONSE_Q_DOWN; 91779fac9c9SMichael Cyr ibmvscsis_free_command_q(vscsi); 91879fac9c9SMichael Cyr } 91979fac9c9SMichael Cyr } else { 92079fac9c9SMichael Cyr vscsi->state = ERR_DISCONNECTED; 92179fac9c9SMichael Cyr vscsi->flags |= RESPONSE_Q_DOWN; 92279fac9c9SMichael Cyr } 92379fac9c9SMichael Cyr } 92479fac9c9SMichael Cyr } 92579fac9c9SMichael Cyr 92679fac9c9SMichael Cyr /** 92779fac9c9SMichael Cyr * ibmvscsis_free_cmd_resources() - Free command resources 92879fac9c9SMichael Cyr * @vscsi: Pointer to our adapter structure 92979fac9c9SMichael Cyr * @cmd: Command which is not longer in use 93079fac9c9SMichael Cyr * 93179fac9c9SMichael Cyr * Must be called with interrupt lock held. 93279fac9c9SMichael Cyr */ 93379fac9c9SMichael Cyr static void ibmvscsis_free_cmd_resources(struct scsi_info *vscsi, 93479fac9c9SMichael Cyr struct ibmvscsis_cmd *cmd) 93579fac9c9SMichael Cyr { 93679fac9c9SMichael Cyr struct iu_entry *iue = cmd->iue; 93779fac9c9SMichael Cyr 93879fac9c9SMichael Cyr switch (cmd->type) { 93979fac9c9SMichael Cyr case TASK_MANAGEMENT: 94079fac9c9SMichael Cyr case SCSI_CDB: 94179fac9c9SMichael Cyr /* 94279fac9c9SMichael Cyr * When the queue goes down this value is cleared, so it 94379fac9c9SMichael Cyr * cannot be cleared in this general purpose function. 94479fac9c9SMichael Cyr */ 94579fac9c9SMichael Cyr if (vscsi->debit) 94679fac9c9SMichael Cyr vscsi->debit -= 1; 94779fac9c9SMichael Cyr break; 94879fac9c9SMichael Cyr case ADAPTER_MAD: 94979fac9c9SMichael Cyr vscsi->flags &= ~PROCESSING_MAD; 95079fac9c9SMichael Cyr break; 95179fac9c9SMichael Cyr case UNSET_TYPE: 95279fac9c9SMichael Cyr break; 95379fac9c9SMichael Cyr default: 95479fac9c9SMichael Cyr dev_err(&vscsi->dev, "free_cmd_resources unknown type %d\n", 95579fac9c9SMichael Cyr cmd->type); 95679fac9c9SMichael Cyr break; 95779fac9c9SMichael Cyr } 95879fac9c9SMichael Cyr 95979fac9c9SMichael Cyr cmd->iue = NULL; 96079fac9c9SMichael Cyr list_add_tail(&cmd->list, &vscsi->free_cmd); 96179fac9c9SMichael Cyr srp_iu_put(iue); 96279fac9c9SMichael Cyr 96379fac9c9SMichael Cyr if (list_empty(&vscsi->active_q) && list_empty(&vscsi->schedule_q) && 96479fac9c9SMichael Cyr list_empty(&vscsi->waiting_rsp) && (vscsi->flags & WAIT_FOR_IDLE)) { 96579fac9c9SMichael Cyr vscsi->flags &= ~WAIT_FOR_IDLE; 96679fac9c9SMichael Cyr complete(&vscsi->wait_idle); 96779fac9c9SMichael Cyr } 96879fac9c9SMichael Cyr } 96979fac9c9SMichael Cyr 97079fac9c9SMichael Cyr /** 971464fd641SMichael Cyr * ibmvscsis_ready_for_suspend() - Helper function to call VIOCTL 972464fd641SMichael Cyr * @vscsi: Pointer to our adapter structure 973464fd641SMichael Cyr * @idle: Indicates whether we were called from adapter_idle. This 974464fd641SMichael Cyr * is important to know if we need to do a disconnect, since if 975464fd641SMichael Cyr * we're called from adapter_idle, we're still processing the 976464fd641SMichael Cyr * current disconnect, so we can't just call post_disconnect. 977464fd641SMichael Cyr * 978464fd641SMichael Cyr * This function is called when the adapter is idle when phyp has sent 979464fd641SMichael Cyr * us a Prepare for Suspend Transport Event. 980464fd641SMichael Cyr * 981464fd641SMichael Cyr * EXECUTION ENVIRONMENT: 982464fd641SMichael Cyr * Process or interrupt environment called with interrupt lock held 983464fd641SMichael Cyr */ 984464fd641SMichael Cyr static long ibmvscsis_ready_for_suspend(struct scsi_info *vscsi, bool idle) 985464fd641SMichael Cyr { 986464fd641SMichael Cyr long rc = 0; 987464fd641SMichael Cyr struct viosrp_crq *crq; 988464fd641SMichael Cyr 989464fd641SMichael Cyr /* See if there is a Resume event in the queue */ 990464fd641SMichael Cyr crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index; 991464fd641SMichael Cyr 992417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "ready_suspend: flags 0x%x, state 0x%hx crq_valid:%x\n", 993464fd641SMichael Cyr vscsi->flags, vscsi->state, (int)crq->valid); 994464fd641SMichael Cyr 995464fd641SMichael Cyr if (!(vscsi->flags & PREP_FOR_SUSPEND_ABORTED) && !(crq->valid)) { 996464fd641SMichael Cyr rc = h_vioctl(vscsi->dds.unit_id, H_READY_FOR_SUSPEND, 0, 0, 0, 997464fd641SMichael Cyr 0, 0); 998464fd641SMichael Cyr if (rc) { 999417dff6cSBryant G. Ly dev_err(&vscsi->dev, "Ready for Suspend Vioctl failed: %ld\n", 1000417dff6cSBryant G. Ly rc); 1001464fd641SMichael Cyr rc = 0; 1002464fd641SMichael Cyr } 1003464fd641SMichael Cyr } else if (((vscsi->flags & PREP_FOR_SUSPEND_OVERWRITE) && 1004464fd641SMichael Cyr (vscsi->flags & PREP_FOR_SUSPEND_ABORTED)) || 1005464fd641SMichael Cyr ((crq->valid) && ((crq->valid != VALID_TRANS_EVENT) || 1006464fd641SMichael Cyr (crq->format != RESUME_FROM_SUSP)))) { 1007464fd641SMichael Cyr if (idle) { 1008464fd641SMichael Cyr vscsi->state = ERR_DISCONNECT_RECONNECT; 1009464fd641SMichael Cyr ibmvscsis_reset_queue(vscsi); 1010464fd641SMichael Cyr rc = -1; 1011464fd641SMichael Cyr } else if (vscsi->state == CONNECTED) { 1012464fd641SMichael Cyr ibmvscsis_post_disconnect(vscsi, 1013464fd641SMichael Cyr ERR_DISCONNECT_RECONNECT, 0); 1014464fd641SMichael Cyr } 1015464fd641SMichael Cyr 1016464fd641SMichael Cyr vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE; 1017464fd641SMichael Cyr 1018464fd641SMichael Cyr if ((crq->valid) && ((crq->valid != VALID_TRANS_EVENT) || 1019464fd641SMichael Cyr (crq->format != RESUME_FROM_SUSP))) 1020417dff6cSBryant G. Ly dev_err(&vscsi->dev, "Invalid element in CRQ after Prepare for Suspend"); 1021464fd641SMichael Cyr } 1022464fd641SMichael Cyr 1023464fd641SMichael Cyr vscsi->flags &= ~(PREP_FOR_SUSPEND_PENDING | PREP_FOR_SUSPEND_ABORTED); 1024464fd641SMichael Cyr 1025464fd641SMichael Cyr return rc; 1026464fd641SMichael Cyr } 1027464fd641SMichael Cyr 1028464fd641SMichael Cyr /** 102988a678bbSBryant G. Ly * ibmvscsis_trans_event() - Handle a Transport Event 103088a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 103188a678bbSBryant G. Ly * @crq: Pointer to CRQ entry containing the Transport Event 103288a678bbSBryant G. Ly * 103388a678bbSBryant G. Ly * Do the logic to close the I_T nexus. This function may not 103488a678bbSBryant G. Ly * behave to specification. 103588a678bbSBryant G. Ly * 103688a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 103788a678bbSBryant G. Ly * Interrupt, interrupt lock held 103888a678bbSBryant G. Ly */ 103988a678bbSBryant G. Ly static long ibmvscsis_trans_event(struct scsi_info *vscsi, 104088a678bbSBryant G. Ly struct viosrp_crq *crq) 104188a678bbSBryant G. Ly { 104288a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 104388a678bbSBryant G. Ly 1044417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "trans_event: format %d, flags 0x%x, state 0x%hx\n", 104588a678bbSBryant G. Ly (int)crq->format, vscsi->flags, vscsi->state); 104688a678bbSBryant G. Ly 104788a678bbSBryant G. Ly switch (crq->format) { 104888a678bbSBryant G. Ly case MIGRATED: 104988a678bbSBryant G. Ly case PARTNER_FAILED: 105088a678bbSBryant G. Ly case PARTNER_DEREGISTER: 105188a678bbSBryant G. Ly ibmvscsis_delete_client_info(vscsi, true); 1052464fd641SMichael Cyr if (crq->format == MIGRATED) 1053464fd641SMichael Cyr vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE; 105488a678bbSBryant G. Ly switch (vscsi->state) { 105588a678bbSBryant G. Ly case NO_QUEUE: 105688a678bbSBryant G. Ly case ERR_DISCONNECTED: 105788a678bbSBryant G. Ly case UNDEFINED: 105888a678bbSBryant G. Ly break; 105988a678bbSBryant G. Ly 106088a678bbSBryant G. Ly case UNCONFIGURING: 106188a678bbSBryant G. Ly vscsi->flags |= (RESPONSE_Q_DOWN | TRANS_EVENT); 106288a678bbSBryant G. Ly break; 106388a678bbSBryant G. Ly 106488a678bbSBryant G. Ly case WAIT_ENABLED: 106588a678bbSBryant G. Ly break; 106688a678bbSBryant G. Ly 106788a678bbSBryant G. Ly case WAIT_CONNECTION: 106888a678bbSBryant G. Ly break; 106988a678bbSBryant G. Ly 107088a678bbSBryant G. Ly case CONNECTED: 107188a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 107288a678bbSBryant G. Ly (RESPONSE_Q_DOWN | 107388a678bbSBryant G. Ly TRANS_EVENT)); 107488a678bbSBryant G. Ly break; 107588a678bbSBryant G. Ly 107688a678bbSBryant G. Ly case SRP_PROCESSING: 107788a678bbSBryant G. Ly if ((vscsi->debit > 0) || 107888a678bbSBryant G. Ly !list_empty(&vscsi->schedule_q) || 107988a678bbSBryant G. Ly !list_empty(&vscsi->waiting_rsp) || 108088a678bbSBryant G. Ly !list_empty(&vscsi->active_q)) { 1081417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "debit %d, sched %d, wait %d, active %d\n", 108288a678bbSBryant G. Ly vscsi->debit, 108388a678bbSBryant G. Ly (int)list_empty(&vscsi->schedule_q), 108488a678bbSBryant G. Ly (int)list_empty(&vscsi->waiting_rsp), 108588a678bbSBryant G. Ly (int)list_empty(&vscsi->active_q)); 1086417dff6cSBryant G. Ly dev_warn(&vscsi->dev, "connection lost with outstanding work\n"); 108788a678bbSBryant G. Ly } else { 1088417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "trans_event: SRP Processing, but no outstanding work\n"); 108988a678bbSBryant G. Ly } 109088a678bbSBryant G. Ly 109188a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 109288a678bbSBryant G. Ly (RESPONSE_Q_DOWN | 109388a678bbSBryant G. Ly TRANS_EVENT)); 109488a678bbSBryant G. Ly break; 109588a678bbSBryant G. Ly 109688a678bbSBryant G. Ly case ERR_DISCONNECT: 109788a678bbSBryant G. Ly case ERR_DISCONNECT_RECONNECT: 109888a678bbSBryant G. Ly case WAIT_IDLE: 109988a678bbSBryant G. Ly vscsi->flags |= (RESPONSE_Q_DOWN | TRANS_EVENT); 110088a678bbSBryant G. Ly break; 110188a678bbSBryant G. Ly } 1102464fd641SMichael Cyr break; 1103464fd641SMichael Cyr 1104464fd641SMichael Cyr case PREPARE_FOR_SUSPEND: 1105417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Prep for Suspend, crq status = 0x%x\n", 1106464fd641SMichael Cyr (int)crq->status); 1107464fd641SMichael Cyr switch (vscsi->state) { 1108464fd641SMichael Cyr case ERR_DISCONNECTED: 1109464fd641SMichael Cyr case WAIT_CONNECTION: 1110464fd641SMichael Cyr case CONNECTED: 1111464fd641SMichael Cyr ibmvscsis_ready_for_suspend(vscsi, false); 1112464fd641SMichael Cyr break; 1113464fd641SMichael Cyr case SRP_PROCESSING: 1114464fd641SMichael Cyr vscsi->resume_state = vscsi->state; 1115464fd641SMichael Cyr vscsi->flags |= PREP_FOR_SUSPEND_PENDING; 1116464fd641SMichael Cyr if (crq->status == CRQ_ENTRY_OVERWRITTEN) 1117464fd641SMichael Cyr vscsi->flags |= PREP_FOR_SUSPEND_OVERWRITE; 1118464fd641SMichael Cyr ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 0); 1119464fd641SMichael Cyr break; 1120464fd641SMichael Cyr case NO_QUEUE: 1121464fd641SMichael Cyr case UNDEFINED: 1122464fd641SMichael Cyr case UNCONFIGURING: 1123464fd641SMichael Cyr case WAIT_ENABLED: 1124464fd641SMichael Cyr case ERR_DISCONNECT: 1125464fd641SMichael Cyr case ERR_DISCONNECT_RECONNECT: 1126464fd641SMichael Cyr case WAIT_IDLE: 1127417dff6cSBryant G. Ly dev_err(&vscsi->dev, "Invalid state for Prepare for Suspend Trans Event: 0x%x\n", 1128464fd641SMichael Cyr vscsi->state); 1129464fd641SMichael Cyr break; 1130464fd641SMichael Cyr } 1131464fd641SMichael Cyr break; 1132464fd641SMichael Cyr 1133464fd641SMichael Cyr case RESUME_FROM_SUSP: 1134417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Resume from Suspend, crq status = 0x%x\n", 1135464fd641SMichael Cyr (int)crq->status); 1136464fd641SMichael Cyr if (vscsi->flags & PREP_FOR_SUSPEND_PENDING) { 1137464fd641SMichael Cyr vscsi->flags |= PREP_FOR_SUSPEND_ABORTED; 1138464fd641SMichael Cyr } else { 1139464fd641SMichael Cyr if ((crq->status == CRQ_ENTRY_OVERWRITTEN) || 1140464fd641SMichael Cyr (vscsi->flags & PREP_FOR_SUSPEND_OVERWRITE)) { 1141464fd641SMichael Cyr ibmvscsis_post_disconnect(vscsi, 1142464fd641SMichael Cyr ERR_DISCONNECT_RECONNECT, 1143464fd641SMichael Cyr 0); 1144464fd641SMichael Cyr vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE; 1145464fd641SMichael Cyr } 1146464fd641SMichael Cyr } 1147464fd641SMichael Cyr break; 1148464fd641SMichael Cyr 1149464fd641SMichael Cyr default: 1150464fd641SMichael Cyr rc = ERROR; 1151464fd641SMichael Cyr dev_err(&vscsi->dev, "trans_event: invalid format %d\n", 1152464fd641SMichael Cyr (uint)crq->format); 1153464fd641SMichael Cyr ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 1154464fd641SMichael Cyr RESPONSE_Q_DOWN); 1155464fd641SMichael Cyr break; 115688a678bbSBryant G. Ly } 115788a678bbSBryant G. Ly 115888a678bbSBryant G. Ly rc = vscsi->flags & SCHEDULE_DISCONNECT; 115988a678bbSBryant G. Ly 1160417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Leaving trans_event: flags 0x%x, state 0x%hx, rc %ld\n", 116188a678bbSBryant G. Ly vscsi->flags, vscsi->state, rc); 116288a678bbSBryant G. Ly 116388a678bbSBryant G. Ly return rc; 116488a678bbSBryant G. Ly } 116588a678bbSBryant G. Ly 116688a678bbSBryant G. Ly /** 116788a678bbSBryant G. Ly * ibmvscsis_poll_cmd_q() - Poll Command Queue 116888a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 116988a678bbSBryant G. Ly * 117088a678bbSBryant G. Ly * Called to handle command elements that may have arrived while 117188a678bbSBryant G. Ly * interrupts were disabled. 117288a678bbSBryant G. Ly * 117388a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 117488a678bbSBryant G. Ly * intr_lock must be held 117588a678bbSBryant G. Ly */ 117688a678bbSBryant G. Ly static void ibmvscsis_poll_cmd_q(struct scsi_info *vscsi) 117788a678bbSBryant G. Ly { 117888a678bbSBryant G. Ly struct viosrp_crq *crq; 117988a678bbSBryant G. Ly long rc; 118088a678bbSBryant G. Ly bool ack = true; 118188a678bbSBryant G. Ly volatile u8 valid; 118288a678bbSBryant G. Ly 1183417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "poll_cmd_q: flags 0x%x, state 0x%hx, q index %ud\n", 118488a678bbSBryant G. Ly vscsi->flags, vscsi->state, vscsi->cmd_q.index); 118588a678bbSBryant G. Ly 118688a678bbSBryant G. Ly rc = vscsi->flags & SCHEDULE_DISCONNECT; 118788a678bbSBryant G. Ly crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index; 118888a678bbSBryant G. Ly valid = crq->valid; 118988a678bbSBryant G. Ly dma_rmb(); 119088a678bbSBryant G. Ly 119188a678bbSBryant G. Ly while (valid) { 119288a678bbSBryant G. Ly poll_work: 119388a678bbSBryant G. Ly vscsi->cmd_q.index = 119488a678bbSBryant G. Ly (vscsi->cmd_q.index + 1) & vscsi->cmd_q.mask; 119588a678bbSBryant G. Ly 119688a678bbSBryant G. Ly if (!rc) { 119788a678bbSBryant G. Ly rc = ibmvscsis_parse_command(vscsi, crq); 119888a678bbSBryant G. Ly } else { 119988a678bbSBryant G. Ly if ((uint)crq->valid == VALID_TRANS_EVENT) { 120088a678bbSBryant G. Ly /* 120188a678bbSBryant G. Ly * must service the transport layer events even 120288a678bbSBryant G. Ly * in an error state, dont break out until all 120388a678bbSBryant G. Ly * the consecutive transport events have been 120488a678bbSBryant G. Ly * processed 120588a678bbSBryant G. Ly */ 120688a678bbSBryant G. Ly rc = ibmvscsis_trans_event(vscsi, crq); 120788a678bbSBryant G. Ly } else if (vscsi->flags & TRANS_EVENT) { 120888a678bbSBryant G. Ly /* 120988a678bbSBryant G. Ly * if a tranport event has occurred leave 121088a678bbSBryant G. Ly * everything but transport events on the queue 121188a678bbSBryant G. Ly */ 1212417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "poll_cmd_q, ignoring\n"); 121388a678bbSBryant G. Ly 121488a678bbSBryant G. Ly /* 121588a678bbSBryant G. Ly * need to decrement the queue index so we can 121688a678bbSBryant G. Ly * look at the elment again 121788a678bbSBryant G. Ly */ 121888a678bbSBryant G. Ly if (vscsi->cmd_q.index) 121988a678bbSBryant G. Ly vscsi->cmd_q.index -= 1; 122088a678bbSBryant G. Ly else 122188a678bbSBryant G. Ly /* 122288a678bbSBryant G. Ly * index is at 0 it just wrapped. 122388a678bbSBryant G. Ly * have it index last element in q 122488a678bbSBryant G. Ly */ 122588a678bbSBryant G. Ly vscsi->cmd_q.index = vscsi->cmd_q.mask; 122688a678bbSBryant G. Ly break; 122788a678bbSBryant G. Ly } 122888a678bbSBryant G. Ly } 122988a678bbSBryant G. Ly 123088a678bbSBryant G. Ly crq->valid = INVALIDATE_CMD_RESP_EL; 123188a678bbSBryant G. Ly 123288a678bbSBryant G. Ly crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index; 123388a678bbSBryant G. Ly valid = crq->valid; 123488a678bbSBryant G. Ly dma_rmb(); 123588a678bbSBryant G. Ly } 123688a678bbSBryant G. Ly 123788a678bbSBryant G. Ly if (!rc) { 123888a678bbSBryant G. Ly if (ack) { 123988a678bbSBryant G. Ly vio_enable_interrupts(vscsi->dma_dev); 124088a678bbSBryant G. Ly ack = false; 1241417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "poll_cmd_q, reenabling interrupts\n"); 124288a678bbSBryant G. Ly } 124388a678bbSBryant G. Ly valid = crq->valid; 124488a678bbSBryant G. Ly dma_rmb(); 124588a678bbSBryant G. Ly if (valid) 124688a678bbSBryant G. Ly goto poll_work; 124788a678bbSBryant G. Ly } 124888a678bbSBryant G. Ly 1249417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Leaving poll_cmd_q: rc %ld\n", rc); 125088a678bbSBryant G. Ly } 125188a678bbSBryant G. Ly 125288a678bbSBryant G. Ly /** 125388a678bbSBryant G. Ly * ibmvscsis_free_cmd_qs() - Free elements in queue 125488a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 125588a678bbSBryant G. Ly * 125688a678bbSBryant G. Ly * Free all of the elements on all queues that are waiting for 125788a678bbSBryant G. Ly * whatever reason. 125888a678bbSBryant G. Ly * 125988a678bbSBryant G. Ly * PRECONDITION: 126088a678bbSBryant G. Ly * Called with interrupt lock held 126188a678bbSBryant G. Ly */ 126288a678bbSBryant G. Ly static void ibmvscsis_free_cmd_qs(struct scsi_info *vscsi) 126388a678bbSBryant G. Ly { 126488a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd, *nxt; 126588a678bbSBryant G. Ly 1266417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "free_cmd_qs: waiting_rsp empty %d, timer starter %d\n", 126788a678bbSBryant G. Ly (int)list_empty(&vscsi->waiting_rsp), 126888a678bbSBryant G. Ly vscsi->rsp_q_timer.started); 126988a678bbSBryant G. Ly 127088a678bbSBryant G. Ly list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp, list) { 127188a678bbSBryant G. Ly list_del(&cmd->list); 127288a678bbSBryant G. Ly ibmvscsis_free_cmd_resources(vscsi, cmd); 127388a678bbSBryant G. Ly } 127488a678bbSBryant G. Ly } 127588a678bbSBryant G. Ly 127688a678bbSBryant G. Ly /** 127788a678bbSBryant G. Ly * ibmvscsis_get_free_cmd() - Get free command from list 127888a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 127988a678bbSBryant G. Ly * 128088a678bbSBryant G. Ly * Must be called with interrupt lock held. 128188a678bbSBryant G. Ly */ 128288a678bbSBryant G. Ly static struct ibmvscsis_cmd *ibmvscsis_get_free_cmd(struct scsi_info *vscsi) 128388a678bbSBryant G. Ly { 128488a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd = NULL; 128588a678bbSBryant G. Ly struct iu_entry *iue; 128688a678bbSBryant G. Ly 128788a678bbSBryant G. Ly iue = srp_iu_get(&vscsi->target); 128888a678bbSBryant G. Ly if (iue) { 128988a678bbSBryant G. Ly cmd = list_first_entry_or_null(&vscsi->free_cmd, 129088a678bbSBryant G. Ly struct ibmvscsis_cmd, list); 129188a678bbSBryant G. Ly if (cmd) { 129298883f1bSBryant G. Ly if (cmd->abort_cmd) 129398883f1bSBryant G. Ly cmd->abort_cmd = NULL; 129425e78531SBryant G. Ly cmd->flags &= ~(DELAY_SEND); 129588a678bbSBryant G. Ly list_del(&cmd->list); 129688a678bbSBryant G. Ly cmd->iue = iue; 129788a678bbSBryant G. Ly cmd->type = UNSET_TYPE; 129888a678bbSBryant G. Ly memset(&cmd->se_cmd, 0, sizeof(cmd->se_cmd)); 129988a678bbSBryant G. Ly } else { 130088a678bbSBryant G. Ly srp_iu_put(iue); 130188a678bbSBryant G. Ly } 130288a678bbSBryant G. Ly } 130388a678bbSBryant G. Ly 130488a678bbSBryant G. Ly return cmd; 130588a678bbSBryant G. Ly } 130688a678bbSBryant G. Ly 130788a678bbSBryant G. Ly /** 130888a678bbSBryant G. Ly * ibmvscsis_adapter_idle() - Helper function to handle idle adapter 130988a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 131088a678bbSBryant G. Ly * 131188a678bbSBryant G. Ly * This function is called when the adapter is idle when the driver 131288a678bbSBryant G. Ly * is attempting to clear an error condition. 131388a678bbSBryant G. Ly * The adapter is considered busy if any of its cmd queues 131488a678bbSBryant G. Ly * are non-empty. This function can be invoked 131588a678bbSBryant G. Ly * from the off level disconnect function. 131688a678bbSBryant G. Ly * 131788a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 131888a678bbSBryant G. Ly * Process environment called with interrupt lock held 131988a678bbSBryant G. Ly */ 132088a678bbSBryant G. Ly static void ibmvscsis_adapter_idle(struct scsi_info *vscsi) 132188a678bbSBryant G. Ly { 132288a678bbSBryant G. Ly int free_qs = false; 1323464fd641SMichael Cyr long rc = 0; 132488a678bbSBryant G. Ly 1325417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "adapter_idle: flags 0x%x, state 0x%hx\n", 1326417dff6cSBryant G. Ly vscsi->flags, vscsi->state); 132788a678bbSBryant G. Ly 132888a678bbSBryant G. Ly /* Only need to free qs if we're disconnecting from client */ 132988a678bbSBryant G. Ly if (vscsi->state != WAIT_CONNECTION || vscsi->flags & TRANS_EVENT) 133088a678bbSBryant G. Ly free_qs = true; 133188a678bbSBryant G. Ly 133288a678bbSBryant G. Ly switch (vscsi->state) { 13338bf11557SMichael Cyr case UNCONFIGURING: 13348bf11557SMichael Cyr ibmvscsis_free_command_q(vscsi); 13358bf11557SMichael Cyr dma_rmb(); 13368bf11557SMichael Cyr isync(); 13378bf11557SMichael Cyr if (vscsi->flags & CFG_SLEEPING) { 13388bf11557SMichael Cyr vscsi->flags &= ~CFG_SLEEPING; 13398bf11557SMichael Cyr complete(&vscsi->unconfig); 13408bf11557SMichael Cyr } 13418bf11557SMichael Cyr break; 134288a678bbSBryant G. Ly case ERR_DISCONNECT_RECONNECT: 1343c9b3379fSMichael Cyr ibmvscsis_reset_queue(vscsi); 1344417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "adapter_idle, disc_rec: flags 0x%x\n", 1345417dff6cSBryant G. Ly vscsi->flags); 134688a678bbSBryant G. Ly break; 134788a678bbSBryant G. Ly 134888a678bbSBryant G. Ly case ERR_DISCONNECT: 134988a678bbSBryant G. Ly ibmvscsis_free_command_q(vscsi); 1350c9b3379fSMichael Cyr vscsi->flags &= ~(SCHEDULE_DISCONNECT | DISCONNECT_SCHEDULED); 135188a678bbSBryant G. Ly vscsi->flags |= RESPONSE_Q_DOWN; 1352c9b3379fSMichael Cyr if (vscsi->tport.enabled) 135388a678bbSBryant G. Ly vscsi->state = ERR_DISCONNECTED; 1354c9b3379fSMichael Cyr else 1355c9b3379fSMichael Cyr vscsi->state = WAIT_ENABLED; 1356417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "adapter_idle, disc: flags 0x%x, state 0x%hx\n", 135788a678bbSBryant G. Ly vscsi->flags, vscsi->state); 135888a678bbSBryant G. Ly break; 135988a678bbSBryant G. Ly 136088a678bbSBryant G. Ly case WAIT_IDLE: 136188a678bbSBryant G. Ly vscsi->rsp_q_timer.timer_pops = 0; 136288a678bbSBryant G. Ly vscsi->debit = 0; 136388a678bbSBryant G. Ly vscsi->credit = 0; 1364464fd641SMichael Cyr if (vscsi->flags & PREP_FOR_SUSPEND_PENDING) { 1365464fd641SMichael Cyr vscsi->state = vscsi->resume_state; 1366464fd641SMichael Cyr vscsi->resume_state = 0; 1367464fd641SMichael Cyr rc = ibmvscsis_ready_for_suspend(vscsi, true); 1368464fd641SMichael Cyr vscsi->flags &= ~DISCONNECT_SCHEDULED; 1369464fd641SMichael Cyr if (rc) 1370464fd641SMichael Cyr break; 1371464fd641SMichael Cyr } else if (vscsi->flags & TRANS_EVENT) { 137288a678bbSBryant G. Ly vscsi->state = WAIT_CONNECTION; 137388a678bbSBryant G. Ly vscsi->flags &= PRESERVE_FLAG_FIELDS; 137488a678bbSBryant G. Ly } else { 137588a678bbSBryant G. Ly vscsi->state = CONNECTED; 137688a678bbSBryant G. Ly vscsi->flags &= ~DISCONNECT_SCHEDULED; 137788a678bbSBryant G. Ly } 137888a678bbSBryant G. Ly 1379417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "adapter_idle, wait: flags 0x%x, state 0x%hx\n", 138088a678bbSBryant G. Ly vscsi->flags, vscsi->state); 138188a678bbSBryant G. Ly ibmvscsis_poll_cmd_q(vscsi); 138288a678bbSBryant G. Ly break; 138388a678bbSBryant G. Ly 138488a678bbSBryant G. Ly case ERR_DISCONNECTED: 138588a678bbSBryant G. Ly vscsi->flags &= ~DISCONNECT_SCHEDULED; 1386417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "adapter_idle, disconnected: flags 0x%x, state 0x%hx\n", 138788a678bbSBryant G. Ly vscsi->flags, vscsi->state); 138888a678bbSBryant G. Ly break; 138988a678bbSBryant G. Ly 139088a678bbSBryant G. Ly default: 139188a678bbSBryant G. Ly dev_err(&vscsi->dev, "adapter_idle: in invalid state %d\n", 139288a678bbSBryant G. Ly vscsi->state); 139388a678bbSBryant G. Ly break; 139488a678bbSBryant G. Ly } 139588a678bbSBryant G. Ly 139688a678bbSBryant G. Ly if (free_qs) 139788a678bbSBryant G. Ly ibmvscsis_free_cmd_qs(vscsi); 139888a678bbSBryant G. Ly 139988a678bbSBryant G. Ly /* 140088a678bbSBryant G. Ly * There is a timing window where we could lose a disconnect request. 140188a678bbSBryant G. Ly * The known path to this window occurs during the DISCONNECT_RECONNECT 140288a678bbSBryant G. Ly * case above: reset_queue calls free_command_q, which will release the 140388a678bbSBryant G. Ly * interrupt lock. During that time, a new post_disconnect call can be 140488a678bbSBryant G. Ly * made with a "more severe" state (DISCONNECT or UNCONFIGURING). 140588a678bbSBryant G. Ly * Because the DISCONNECT_SCHEDULED flag is already set, post_disconnect 140688a678bbSBryant G. Ly * will only set the new_state. Now free_command_q reacquires the intr 140788a678bbSBryant G. Ly * lock and clears the DISCONNECT_SCHEDULED flag (using PRESERVE_FLAG_ 140888a678bbSBryant G. Ly * FIELDS), and the disconnect is lost. This is particularly bad when 140988a678bbSBryant G. Ly * the new disconnect was for UNCONFIGURING, since the unconfigure hangs 141088a678bbSBryant G. Ly * forever. 141188a678bbSBryant G. Ly * Fix is that free command queue sets acr state and acr flags if there 141288a678bbSBryant G. Ly * is a change under the lock 141388a678bbSBryant G. Ly * note free command queue writes to this state it clears it 141488a678bbSBryant G. Ly * before releasing the lock, different drivers call the free command 141588a678bbSBryant G. Ly * queue different times so dont initialize above 141688a678bbSBryant G. Ly */ 141788a678bbSBryant G. Ly if (vscsi->phyp_acr_state != 0) { 141888a678bbSBryant G. Ly /* 141988a678bbSBryant G. Ly * set any bits in flags that may have been cleared by 142088a678bbSBryant G. Ly * a call to free command queue in switch statement 142188a678bbSBryant G. Ly * or reset queue 142288a678bbSBryant G. Ly */ 142388a678bbSBryant G. Ly vscsi->flags |= vscsi->phyp_acr_flags; 142488a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, vscsi->phyp_acr_state, 0); 142588a678bbSBryant G. Ly vscsi->phyp_acr_state = 0; 142688a678bbSBryant G. Ly vscsi->phyp_acr_flags = 0; 142788a678bbSBryant G. Ly 1428417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "adapter_idle: flags 0x%x, state 0x%hx, acr_flags 0x%x, acr_state 0x%hx\n", 142988a678bbSBryant G. Ly vscsi->flags, vscsi->state, vscsi->phyp_acr_flags, 143088a678bbSBryant G. Ly vscsi->phyp_acr_state); 143188a678bbSBryant G. Ly } 143288a678bbSBryant G. Ly 1433417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Leaving adapter_idle: flags 0x%x, state 0x%hx, new_state 0x%x\n", 143488a678bbSBryant G. Ly vscsi->flags, vscsi->state, vscsi->new_state); 143588a678bbSBryant G. Ly } 143688a678bbSBryant G. Ly 143788a678bbSBryant G. Ly /** 143888a678bbSBryant G. Ly * ibmvscsis_copy_crq_packet() - Copy CRQ Packet 143988a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 144088a678bbSBryant G. Ly * @cmd: Pointer to command element to use to process the request 144188a678bbSBryant G. Ly * @crq: Pointer to CRQ entry containing the request 144288a678bbSBryant G. Ly * 144388a678bbSBryant G. Ly * Copy the srp information unit from the hosted 144488a678bbSBryant G. Ly * partition using remote dma 144588a678bbSBryant G. Ly * 144688a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 144788a678bbSBryant G. Ly * Interrupt, interrupt lock held 144888a678bbSBryant G. Ly */ 144988a678bbSBryant G. Ly static long ibmvscsis_copy_crq_packet(struct scsi_info *vscsi, 145088a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd, 145188a678bbSBryant G. Ly struct viosrp_crq *crq) 145288a678bbSBryant G. Ly { 145388a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 145488a678bbSBryant G. Ly long rc = 0; 145588a678bbSBryant G. Ly u16 len; 145688a678bbSBryant G. Ly 145788a678bbSBryant G. Ly len = be16_to_cpu(crq->IU_length); 145888a678bbSBryant G. Ly if ((len > SRP_MAX_IU_LEN) || (len == 0)) { 145988a678bbSBryant G. Ly dev_err(&vscsi->dev, "copy_crq: Invalid len %d passed", len); 146088a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 146188a678bbSBryant G. Ly return SRP_VIOLATION; 146288a678bbSBryant G. Ly } 146388a678bbSBryant G. Ly 146488a678bbSBryant G. Ly rc = h_copy_rdma(len, vscsi->dds.window[REMOTE].liobn, 146588a678bbSBryant G. Ly be64_to_cpu(crq->IU_data_ptr), 146688a678bbSBryant G. Ly vscsi->dds.window[LOCAL].liobn, iue->sbuf->dma); 146788a678bbSBryant G. Ly 146888a678bbSBryant G. Ly switch (rc) { 146988a678bbSBryant G. Ly case H_SUCCESS: 147088a678bbSBryant G. Ly cmd->init_time = mftb(); 147188a678bbSBryant G. Ly iue->remote_token = crq->IU_data_ptr; 147288a678bbSBryant G. Ly iue->iu_len = len; 1473417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "copy_crq: ioba 0x%llx, init_time 0x%llx\n", 147488a678bbSBryant G. Ly be64_to_cpu(crq->IU_data_ptr), cmd->init_time); 147588a678bbSBryant G. Ly break; 147688a678bbSBryant G. Ly case H_PERMISSION: 147788a678bbSBryant G. Ly if (connection_broken(vscsi)) 147888a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, 147988a678bbSBryant G. Ly ERR_DISCONNECT_RECONNECT, 148088a678bbSBryant G. Ly (RESPONSE_Q_DOWN | 148188a678bbSBryant G. Ly CLIENT_FAILED)); 148288a678bbSBryant G. Ly else 148388a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, 148488a678bbSBryant G. Ly ERR_DISCONNECT_RECONNECT, 0); 148588a678bbSBryant G. Ly 148688a678bbSBryant G. Ly dev_err(&vscsi->dev, "copy_crq: h_copy_rdma failed, rc %ld\n", 148788a678bbSBryant G. Ly rc); 148888a678bbSBryant G. Ly break; 148988a678bbSBryant G. Ly case H_DEST_PARM: 149088a678bbSBryant G. Ly case H_SOURCE_PARM: 149188a678bbSBryant G. Ly default: 149288a678bbSBryant G. Ly dev_err(&vscsi->dev, "copy_crq: h_copy_rdma failed, rc %ld\n", 149388a678bbSBryant G. Ly rc); 149488a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 149588a678bbSBryant G. Ly break; 149688a678bbSBryant G. Ly } 149788a678bbSBryant G. Ly 149888a678bbSBryant G. Ly return rc; 149988a678bbSBryant G. Ly } 150088a678bbSBryant G. Ly 150188a678bbSBryant G. Ly /** 150288a678bbSBryant G. Ly * ibmvscsis_adapter_info - Service an Adapter Info MAnagement Data gram 150388a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 150488a678bbSBryant G. Ly * @iue: Information Unit containing the Adapter Info MAD request 150588a678bbSBryant G. Ly * 150688a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 150779fac9c9SMichael Cyr * Interrupt adapter lock is held 150888a678bbSBryant G. Ly */ 150988a678bbSBryant G. Ly static long ibmvscsis_adapter_info(struct scsi_info *vscsi, 151088a678bbSBryant G. Ly struct iu_entry *iue) 151188a678bbSBryant G. Ly { 151288a678bbSBryant G. Ly struct viosrp_adapter_info *mad = &vio_iu(iue)->mad.adapter_info; 151388a678bbSBryant G. Ly struct mad_adapter_info_data *info; 151488a678bbSBryant G. Ly uint flag_bits = 0; 151588a678bbSBryant G. Ly dma_addr_t token; 151688a678bbSBryant G. Ly long rc; 151788a678bbSBryant G. Ly 151888a678bbSBryant G. Ly mad->common.status = cpu_to_be16(VIOSRP_MAD_SUCCESS); 151988a678bbSBryant G. Ly 152088a678bbSBryant G. Ly if (be16_to_cpu(mad->common.length) > sizeof(*info)) { 152188a678bbSBryant G. Ly mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED); 152288a678bbSBryant G. Ly return 0; 152388a678bbSBryant G. Ly } 152488a678bbSBryant G. Ly 152588a678bbSBryant G. Ly info = dma_alloc_coherent(&vscsi->dma_dev->dev, sizeof(*info), &token, 1526a5b0e406SBryant G. Ly GFP_ATOMIC); 152788a678bbSBryant G. Ly if (!info) { 152888a678bbSBryant G. Ly dev_err(&vscsi->dev, "bad dma_alloc_coherent %p\n", 152988a678bbSBryant G. Ly iue->target); 153088a678bbSBryant G. Ly mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED); 153188a678bbSBryant G. Ly return 0; 153288a678bbSBryant G. Ly } 153388a678bbSBryant G. Ly 153488a678bbSBryant G. Ly /* Get remote info */ 153588a678bbSBryant G. Ly rc = h_copy_rdma(be16_to_cpu(mad->common.length), 153688a678bbSBryant G. Ly vscsi->dds.window[REMOTE].liobn, 153788a678bbSBryant G. Ly be64_to_cpu(mad->buffer), 153888a678bbSBryant G. Ly vscsi->dds.window[LOCAL].liobn, token); 153988a678bbSBryant G. Ly 154088a678bbSBryant G. Ly if (rc != H_SUCCESS) { 154188a678bbSBryant G. Ly if (rc == H_PERMISSION) { 154288a678bbSBryant G. Ly if (connection_broken(vscsi)) 154388a678bbSBryant G. Ly flag_bits = (RESPONSE_Q_DOWN | CLIENT_FAILED); 154488a678bbSBryant G. Ly } 1545417dff6cSBryant G. Ly dev_warn(&vscsi->dev, "adapter_info: h_copy_rdma from client failed, rc %ld\n", 154688a678bbSBryant G. Ly rc); 1547417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "adapter_info: ioba 0x%llx, flags 0x%x, flag_bits 0x%x\n", 154888a678bbSBryant G. Ly be64_to_cpu(mad->buffer), vscsi->flags, flag_bits); 154988a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 155088a678bbSBryant G. Ly flag_bits); 155188a678bbSBryant G. Ly goto free_dma; 155288a678bbSBryant G. Ly } 155388a678bbSBryant G. Ly 155488a678bbSBryant G. Ly /* 155588a678bbSBryant G. Ly * Copy client info, but ignore partition number, which we 155688a678bbSBryant G. Ly * already got from phyp - unless we failed to get it from 155788a678bbSBryant G. Ly * phyp (e.g. if we're running on a p5 system). 155888a678bbSBryant G. Ly */ 155988a678bbSBryant G. Ly if (vscsi->client_data.partition_number == 0) 156088a678bbSBryant G. Ly vscsi->client_data.partition_number = 156188a678bbSBryant G. Ly be32_to_cpu(info->partition_number); 156288a678bbSBryant G. Ly strncpy(vscsi->client_data.srp_version, info->srp_version, 156388a678bbSBryant G. Ly sizeof(vscsi->client_data.srp_version)); 156488a678bbSBryant G. Ly strncpy(vscsi->client_data.partition_name, info->partition_name, 156588a678bbSBryant G. Ly sizeof(vscsi->client_data.partition_name)); 156688a678bbSBryant G. Ly vscsi->client_data.mad_version = be32_to_cpu(info->mad_version); 156788a678bbSBryant G. Ly vscsi->client_data.os_type = be32_to_cpu(info->os_type); 156888a678bbSBryant G. Ly 156988a678bbSBryant G. Ly /* Copy our info */ 157088a678bbSBryant G. Ly strncpy(info->srp_version, SRP_VERSION, 157188a678bbSBryant G. Ly sizeof(info->srp_version)); 157288a678bbSBryant G. Ly strncpy(info->partition_name, vscsi->dds.partition_name, 157388a678bbSBryant G. Ly sizeof(info->partition_name)); 157488a678bbSBryant G. Ly info->partition_number = cpu_to_be32(vscsi->dds.partition_num); 157588a678bbSBryant G. Ly info->mad_version = cpu_to_be32(MAD_VERSION_1); 157688a678bbSBryant G. Ly info->os_type = cpu_to_be32(LINUX); 157788a678bbSBryant G. Ly memset(&info->port_max_txu[0], 0, sizeof(info->port_max_txu)); 1578387b978cSBryant G. Ly info->port_max_txu[0] = cpu_to_be32(MAX_TXU); 157988a678bbSBryant G. Ly 158088a678bbSBryant G. Ly dma_wmb(); 158188a678bbSBryant G. Ly rc = h_copy_rdma(sizeof(*info), vscsi->dds.window[LOCAL].liobn, 158288a678bbSBryant G. Ly token, vscsi->dds.window[REMOTE].liobn, 158388a678bbSBryant G. Ly be64_to_cpu(mad->buffer)); 158488a678bbSBryant G. Ly switch (rc) { 158588a678bbSBryant G. Ly case H_SUCCESS: 158688a678bbSBryant G. Ly break; 158788a678bbSBryant G. Ly 158888a678bbSBryant G. Ly case H_SOURCE_PARM: 158988a678bbSBryant G. Ly case H_DEST_PARM: 159088a678bbSBryant G. Ly case H_PERMISSION: 159188a678bbSBryant G. Ly if (connection_broken(vscsi)) 159288a678bbSBryant G. Ly flag_bits = (RESPONSE_Q_DOWN | CLIENT_FAILED); 159388a678bbSBryant G. Ly default: 159488a678bbSBryant G. Ly dev_err(&vscsi->dev, "adapter_info: h_copy_rdma to client failed, rc %ld\n", 159588a678bbSBryant G. Ly rc); 159688a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, 159788a678bbSBryant G. Ly ERR_DISCONNECT_RECONNECT, 159888a678bbSBryant G. Ly flag_bits); 159988a678bbSBryant G. Ly break; 160088a678bbSBryant G. Ly } 160188a678bbSBryant G. Ly 160288a678bbSBryant G. Ly free_dma: 160388a678bbSBryant G. Ly dma_free_coherent(&vscsi->dma_dev->dev, sizeof(*info), info, token); 1604417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Leaving adapter_info, rc %ld\n", rc); 160588a678bbSBryant G. Ly 160688a678bbSBryant G. Ly return rc; 160788a678bbSBryant G. Ly } 160888a678bbSBryant G. Ly 160988a678bbSBryant G. Ly /** 161088a678bbSBryant G. Ly * ibmvscsis_cap_mad() - Service a Capabilities MAnagement Data gram 161188a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 161288a678bbSBryant G. Ly * @iue: Information Unit containing the Capabilities MAD request 161388a678bbSBryant G. Ly * 161488a678bbSBryant G. Ly * NOTE: if you return an error from this routine you must be 161588a678bbSBryant G. Ly * disconnecting or you will cause a hang 161688a678bbSBryant G. Ly * 161788a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 161888a678bbSBryant G. Ly * Interrupt called with adapter lock held 161988a678bbSBryant G. Ly */ 162088a678bbSBryant G. Ly static int ibmvscsis_cap_mad(struct scsi_info *vscsi, struct iu_entry *iue) 162188a678bbSBryant G. Ly { 162288a678bbSBryant G. Ly struct viosrp_capabilities *mad = &vio_iu(iue)->mad.capabilities; 162388a678bbSBryant G. Ly struct capabilities *cap; 162488a678bbSBryant G. Ly struct mad_capability_common *common; 162588a678bbSBryant G. Ly dma_addr_t token; 162688a678bbSBryant G. Ly u16 olen, len, status, min_len, cap_len; 162788a678bbSBryant G. Ly u32 flag; 162888a678bbSBryant G. Ly uint flag_bits = 0; 162988a678bbSBryant G. Ly long rc = 0; 163088a678bbSBryant G. Ly 163188a678bbSBryant G. Ly olen = be16_to_cpu(mad->common.length); 163288a678bbSBryant G. Ly /* 163388a678bbSBryant G. Ly * struct capabilities hardcodes a couple capabilities after the 163488a678bbSBryant G. Ly * header, but the capabilities can actually be in any order. 163588a678bbSBryant G. Ly */ 163688a678bbSBryant G. Ly min_len = offsetof(struct capabilities, migration); 163788a678bbSBryant G. Ly if ((olen < min_len) || (olen > PAGE_SIZE)) { 1638417dff6cSBryant G. Ly dev_warn(&vscsi->dev, "cap_mad: invalid len %d\n", olen); 163988a678bbSBryant G. Ly mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED); 164088a678bbSBryant G. Ly return 0; 164188a678bbSBryant G. Ly } 164288a678bbSBryant G. Ly 164388a678bbSBryant G. Ly cap = dma_alloc_coherent(&vscsi->dma_dev->dev, olen, &token, 1644a5b0e406SBryant G. Ly GFP_ATOMIC); 164588a678bbSBryant G. Ly if (!cap) { 164688a678bbSBryant G. Ly dev_err(&vscsi->dev, "bad dma_alloc_coherent %p\n", 164788a678bbSBryant G. Ly iue->target); 164888a678bbSBryant G. Ly mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED); 164988a678bbSBryant G. Ly return 0; 165088a678bbSBryant G. Ly } 165188a678bbSBryant G. Ly rc = h_copy_rdma(olen, vscsi->dds.window[REMOTE].liobn, 165288a678bbSBryant G. Ly be64_to_cpu(mad->buffer), 165388a678bbSBryant G. Ly vscsi->dds.window[LOCAL].liobn, token); 165488a678bbSBryant G. Ly if (rc == H_SUCCESS) { 165588a678bbSBryant G. Ly strncpy(cap->name, dev_name(&vscsi->dma_dev->dev), 165688a678bbSBryant G. Ly SRP_MAX_LOC_LEN); 165788a678bbSBryant G. Ly 165888a678bbSBryant G. Ly len = olen - min_len; 165988a678bbSBryant G. Ly status = VIOSRP_MAD_SUCCESS; 166088a678bbSBryant G. Ly common = (struct mad_capability_common *)&cap->migration; 166188a678bbSBryant G. Ly 166288a678bbSBryant G. Ly while ((len > 0) && (status == VIOSRP_MAD_SUCCESS) && !rc) { 1663417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "cap_mad: len left %hd, cap type %d, cap len %hd\n", 166488a678bbSBryant G. Ly len, be32_to_cpu(common->cap_type), 166588a678bbSBryant G. Ly be16_to_cpu(common->length)); 166688a678bbSBryant G. Ly 166788a678bbSBryant G. Ly cap_len = be16_to_cpu(common->length); 166888a678bbSBryant G. Ly if (cap_len > len) { 166988a678bbSBryant G. Ly dev_err(&vscsi->dev, "cap_mad: cap len mismatch with total len\n"); 167088a678bbSBryant G. Ly status = VIOSRP_MAD_FAILED; 167188a678bbSBryant G. Ly break; 167288a678bbSBryant G. Ly } 167388a678bbSBryant G. Ly 167488a678bbSBryant G. Ly if (cap_len == 0) { 167588a678bbSBryant G. Ly dev_err(&vscsi->dev, "cap_mad: cap len is 0\n"); 167688a678bbSBryant G. Ly status = VIOSRP_MAD_FAILED; 167788a678bbSBryant G. Ly break; 167888a678bbSBryant G. Ly } 167988a678bbSBryant G. Ly 168088a678bbSBryant G. Ly switch (common->cap_type) { 168188a678bbSBryant G. Ly default: 1682417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "cap_mad: unsupported capability\n"); 168388a678bbSBryant G. Ly common->server_support = 0; 168488a678bbSBryant G. Ly flag = cpu_to_be32((u32)CAP_LIST_SUPPORTED); 168588a678bbSBryant G. Ly cap->flags &= ~flag; 168688a678bbSBryant G. Ly break; 168788a678bbSBryant G. Ly } 168888a678bbSBryant G. Ly 168988a678bbSBryant G. Ly len = len - cap_len; 169088a678bbSBryant G. Ly common = (struct mad_capability_common *) 169188a678bbSBryant G. Ly ((char *)common + cap_len); 169288a678bbSBryant G. Ly } 169388a678bbSBryant G. Ly 169488a678bbSBryant G. Ly mad->common.status = cpu_to_be16(status); 169588a678bbSBryant G. Ly 169688a678bbSBryant G. Ly dma_wmb(); 169788a678bbSBryant G. Ly rc = h_copy_rdma(olen, vscsi->dds.window[LOCAL].liobn, token, 169888a678bbSBryant G. Ly vscsi->dds.window[REMOTE].liobn, 169988a678bbSBryant G. Ly be64_to_cpu(mad->buffer)); 170088a678bbSBryant G. Ly 170188a678bbSBryant G. Ly if (rc != H_SUCCESS) { 1702417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "cap_mad: failed to copy to client, rc %ld\n", 170388a678bbSBryant G. Ly rc); 170488a678bbSBryant G. Ly 170588a678bbSBryant G. Ly if (rc == H_PERMISSION) { 170688a678bbSBryant G. Ly if (connection_broken(vscsi)) 170788a678bbSBryant G. Ly flag_bits = (RESPONSE_Q_DOWN | 170888a678bbSBryant G. Ly CLIENT_FAILED); 170988a678bbSBryant G. Ly } 171088a678bbSBryant G. Ly 1711417dff6cSBryant G. Ly dev_warn(&vscsi->dev, "cap_mad: error copying data to client, rc %ld\n", 171288a678bbSBryant G. Ly rc); 171388a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, 171488a678bbSBryant G. Ly ERR_DISCONNECT_RECONNECT, 171588a678bbSBryant G. Ly flag_bits); 171688a678bbSBryant G. Ly } 171788a678bbSBryant G. Ly } 171888a678bbSBryant G. Ly 171988a678bbSBryant G. Ly dma_free_coherent(&vscsi->dma_dev->dev, olen, cap, token); 172088a678bbSBryant G. Ly 1721417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Leaving cap_mad, rc %ld, client_cap 0x%x\n", 172288a678bbSBryant G. Ly rc, vscsi->client_cap); 172388a678bbSBryant G. Ly 172488a678bbSBryant G. Ly return rc; 172588a678bbSBryant G. Ly } 172688a678bbSBryant G. Ly 172788a678bbSBryant G. Ly /** 172888a678bbSBryant G. Ly * ibmvscsis_process_mad() - Service a MAnagement Data gram 172988a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 173088a678bbSBryant G. Ly * @iue: Information Unit containing the MAD request 173188a678bbSBryant G. Ly * 173288a678bbSBryant G. Ly * Must be called with interrupt lock held. 173388a678bbSBryant G. Ly */ 173488a678bbSBryant G. Ly static long ibmvscsis_process_mad(struct scsi_info *vscsi, struct iu_entry *iue) 173588a678bbSBryant G. Ly { 173688a678bbSBryant G. Ly struct mad_common *mad = (struct mad_common *)&vio_iu(iue)->mad; 173788a678bbSBryant G. Ly struct viosrp_empty_iu *empty; 173888a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 173988a678bbSBryant G. Ly 174088a678bbSBryant G. Ly switch (be32_to_cpu(mad->type)) { 174188a678bbSBryant G. Ly case VIOSRP_EMPTY_IU_TYPE: 174288a678bbSBryant G. Ly empty = &vio_iu(iue)->mad.empty_iu; 174388a678bbSBryant G. Ly vscsi->empty_iu_id = be64_to_cpu(empty->buffer); 174488a678bbSBryant G. Ly vscsi->empty_iu_tag = be64_to_cpu(empty->common.tag); 174588a678bbSBryant G. Ly mad->status = cpu_to_be16(VIOSRP_MAD_SUCCESS); 174688a678bbSBryant G. Ly break; 174788a678bbSBryant G. Ly case VIOSRP_ADAPTER_INFO_TYPE: 174888a678bbSBryant G. Ly rc = ibmvscsis_adapter_info(vscsi, iue); 174988a678bbSBryant G. Ly break; 175088a678bbSBryant G. Ly case VIOSRP_CAPABILITIES_TYPE: 175188a678bbSBryant G. Ly rc = ibmvscsis_cap_mad(vscsi, iue); 175288a678bbSBryant G. Ly break; 175388a678bbSBryant G. Ly case VIOSRP_ENABLE_FAST_FAIL: 175488a678bbSBryant G. Ly if (vscsi->state == CONNECTED) { 175588a678bbSBryant G. Ly vscsi->fast_fail = true; 175688a678bbSBryant G. Ly mad->status = cpu_to_be16(VIOSRP_MAD_SUCCESS); 175788a678bbSBryant G. Ly } else { 1758417dff6cSBryant G. Ly dev_warn(&vscsi->dev, "fast fail mad sent after login\n"); 175988a678bbSBryant G. Ly mad->status = cpu_to_be16(VIOSRP_MAD_FAILED); 176088a678bbSBryant G. Ly } 176188a678bbSBryant G. Ly break; 176288a678bbSBryant G. Ly default: 176388a678bbSBryant G. Ly mad->status = cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED); 176488a678bbSBryant G. Ly break; 176588a678bbSBryant G. Ly } 176688a678bbSBryant G. Ly 176788a678bbSBryant G. Ly return rc; 176888a678bbSBryant G. Ly } 176988a678bbSBryant G. Ly 177088a678bbSBryant G. Ly /** 177188a678bbSBryant G. Ly * srp_snd_msg_failed() - Handle an error when sending a response 177288a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 177388a678bbSBryant G. Ly * @rc: The return code from the h_send_crq command 177488a678bbSBryant G. Ly * 177588a678bbSBryant G. Ly * Must be called with interrupt lock held. 177688a678bbSBryant G. Ly */ 177788a678bbSBryant G. Ly static void srp_snd_msg_failed(struct scsi_info *vscsi, long rc) 177888a678bbSBryant G. Ly { 177988a678bbSBryant G. Ly ktime_t kt; 178088a678bbSBryant G. Ly 178188a678bbSBryant G. Ly if (rc != H_DROPPED) { 178288a678bbSBryant G. Ly ibmvscsis_free_cmd_qs(vscsi); 178388a678bbSBryant G. Ly 178488a678bbSBryant G. Ly if (rc == H_CLOSED) 178588a678bbSBryant G. Ly vscsi->flags |= CLIENT_FAILED; 178688a678bbSBryant G. Ly 178788a678bbSBryant G. Ly /* don't flag the same problem multiple times */ 178888a678bbSBryant G. Ly if (!(vscsi->flags & RESPONSE_Q_DOWN)) { 178988a678bbSBryant G. Ly vscsi->flags |= RESPONSE_Q_DOWN; 179088a678bbSBryant G. Ly if (!(vscsi->state & (ERR_DISCONNECT | 179188a678bbSBryant G. Ly ERR_DISCONNECT_RECONNECT | 179288a678bbSBryant G. Ly ERR_DISCONNECTED | UNDEFINED))) { 179388a678bbSBryant G. Ly dev_err(&vscsi->dev, "snd_msg_failed: setting RESPONSE_Q_DOWN, state 0x%hx, flags 0x%x, rc %ld\n", 179488a678bbSBryant G. Ly vscsi->state, vscsi->flags, rc); 179588a678bbSBryant G. Ly } 179688a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, 179788a678bbSBryant G. Ly ERR_DISCONNECT_RECONNECT, 0); 179888a678bbSBryant G. Ly } 179988a678bbSBryant G. Ly return; 180088a678bbSBryant G. Ly } 180188a678bbSBryant G. Ly 180288a678bbSBryant G. Ly /* 180388a678bbSBryant G. Ly * The response queue is full. 180488a678bbSBryant G. Ly * If the server is processing SRP requests, i.e. 180588a678bbSBryant G. Ly * the client has successfully done an 180688a678bbSBryant G. Ly * SRP_LOGIN, then it will wait forever for room in 180788a678bbSBryant G. Ly * the queue. However if the system admin 180888a678bbSBryant G. Ly * is attempting to unconfigure the server then one 180988a678bbSBryant G. Ly * or more children will be in a state where 181088a678bbSBryant G. Ly * they are being removed. So if there is even one 181188a678bbSBryant G. Ly * child being removed then the driver assumes 181288a678bbSBryant G. Ly * the system admin is attempting to break the 181388a678bbSBryant G. Ly * connection with the client and MAX_TIMER_POPS 181488a678bbSBryant G. Ly * is honored. 181588a678bbSBryant G. Ly */ 181688a678bbSBryant G. Ly if ((vscsi->rsp_q_timer.timer_pops < MAX_TIMER_POPS) || 181788a678bbSBryant G. Ly (vscsi->state == SRP_PROCESSING)) { 1818417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "snd_msg_failed: response queue full, flags 0x%x, timer started %d, pops %d\n", 181988a678bbSBryant G. Ly vscsi->flags, (int)vscsi->rsp_q_timer.started, 182088a678bbSBryant G. Ly vscsi->rsp_q_timer.timer_pops); 182188a678bbSBryant G. Ly 182288a678bbSBryant G. Ly /* 182388a678bbSBryant G. Ly * Check if the timer is running; if it 182488a678bbSBryant G. Ly * is not then start it up. 182588a678bbSBryant G. Ly */ 182688a678bbSBryant G. Ly if (!vscsi->rsp_q_timer.started) { 182788a678bbSBryant G. Ly if (vscsi->rsp_q_timer.timer_pops < 182888a678bbSBryant G. Ly MAX_TIMER_POPS) { 18298b0e1953SThomas Gleixner kt = WAIT_NANO_SECONDS; 183088a678bbSBryant G. Ly } else { 183188a678bbSBryant G. Ly /* 183288a678bbSBryant G. Ly * slide the timeslice if the maximum 183388a678bbSBryant G. Ly * timer pops have already happened 183488a678bbSBryant G. Ly */ 183588a678bbSBryant G. Ly kt = ktime_set(WAIT_SECONDS, 0); 183688a678bbSBryant G. Ly } 183788a678bbSBryant G. Ly 183888a678bbSBryant G. Ly vscsi->rsp_q_timer.started = true; 183988a678bbSBryant G. Ly hrtimer_start(&vscsi->rsp_q_timer.timer, kt, 184088a678bbSBryant G. Ly HRTIMER_MODE_REL); 184188a678bbSBryant G. Ly } 184288a678bbSBryant G. Ly } else { 184388a678bbSBryant G. Ly /* 184488a678bbSBryant G. Ly * TBD: Do we need to worry about this? Need to get 184588a678bbSBryant G. Ly * remove working. 184688a678bbSBryant G. Ly */ 184788a678bbSBryant G. Ly /* 184888a678bbSBryant G. Ly * waited a long time and it appears the system admin 184988a678bbSBryant G. Ly * is bring this driver down 185088a678bbSBryant G. Ly */ 185188a678bbSBryant G. Ly vscsi->flags |= RESPONSE_Q_DOWN; 185288a678bbSBryant G. Ly ibmvscsis_free_cmd_qs(vscsi); 185388a678bbSBryant G. Ly /* 185488a678bbSBryant G. Ly * if the driver is already attempting to disconnect 185588a678bbSBryant G. Ly * from the client and has already logged an error 185688a678bbSBryant G. Ly * trace this event but don't put it in the error log 185788a678bbSBryant G. Ly */ 185888a678bbSBryant G. Ly if (!(vscsi->state & (ERR_DISCONNECT | 185988a678bbSBryant G. Ly ERR_DISCONNECT_RECONNECT | 186088a678bbSBryant G. Ly ERR_DISCONNECTED | UNDEFINED))) { 186188a678bbSBryant G. Ly dev_err(&vscsi->dev, "client crq full too long\n"); 186288a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, 186388a678bbSBryant G. Ly ERR_DISCONNECT_RECONNECT, 186488a678bbSBryant G. Ly 0); 186588a678bbSBryant G. Ly } 186688a678bbSBryant G. Ly } 186788a678bbSBryant G. Ly } 186888a678bbSBryant G. Ly 186988a678bbSBryant G. Ly /** 187088a678bbSBryant G. Ly * ibmvscsis_send_messages() - Send a Response 187188a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 187288a678bbSBryant G. Ly * 187388a678bbSBryant G. Ly * Send a response, first checking the waiting queue. Responses are 187488a678bbSBryant G. Ly * sent in order they are received. If the response cannot be sent, 187588a678bbSBryant G. Ly * because the client queue is full, it stays on the waiting queue. 187688a678bbSBryant G. Ly * 187788a678bbSBryant G. Ly * PRECONDITION: 187888a678bbSBryant G. Ly * Called with interrupt lock held 187988a678bbSBryant G. Ly */ 188088a678bbSBryant G. Ly static void ibmvscsis_send_messages(struct scsi_info *vscsi) 188188a678bbSBryant G. Ly { 188288a678bbSBryant G. Ly u64 msg_hi = 0; 188325e78531SBryant G. Ly /* note do not attempt to access the IU_data_ptr with this pointer 188488a678bbSBryant G. Ly * it is not valid 188588a678bbSBryant G. Ly */ 188688a678bbSBryant G. Ly struct viosrp_crq *crq = (struct viosrp_crq *)&msg_hi; 188788a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd, *nxt; 188888a678bbSBryant G. Ly struct iu_entry *iue; 188988a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 189025e78531SBryant G. Ly bool retry = false; 189188a678bbSBryant G. Ly 189288a678bbSBryant G. Ly if (!(vscsi->flags & RESPONSE_Q_DOWN)) { 189325e78531SBryant G. Ly do { 189425e78531SBryant G. Ly retry = false; 189525e78531SBryant G. Ly list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp, 189625e78531SBryant G. Ly list) { 189725e78531SBryant G. Ly /* 189825e78531SBryant G. Ly * Check to make sure abort cmd gets processed 189925e78531SBryant G. Ly * prior to the abort tmr cmd 190025e78531SBryant G. Ly */ 190125e78531SBryant G. Ly if (cmd->flags & DELAY_SEND) 190225e78531SBryant G. Ly continue; 190325e78531SBryant G. Ly 190425e78531SBryant G. Ly if (cmd->abort_cmd) { 190525e78531SBryant G. Ly retry = true; 190625e78531SBryant G. Ly cmd->abort_cmd->flags &= ~(DELAY_SEND); 190798883f1bSBryant G. Ly cmd->abort_cmd = NULL; 190825e78531SBryant G. Ly } 190925e78531SBryant G. Ly 191025e78531SBryant G. Ly /* 191125e78531SBryant G. Ly * If CMD_T_ABORTED w/o CMD_T_TAS scenarios and 191225e78531SBryant G. Ly * the case where LIO issued a 191325e78531SBryant G. Ly * ABORT_TASK: Sending TMR_TASK_DOES_NOT_EXIST 191425e78531SBryant G. Ly * case then we dont send a response, since it 191525e78531SBryant G. Ly * was already done. 191625e78531SBryant G. Ly */ 191725e78531SBryant G. Ly if (cmd->se_cmd.transport_state & CMD_T_ABORTED && 191825e78531SBryant G. Ly !(cmd->se_cmd.transport_state & CMD_T_TAS)) { 191925e78531SBryant G. Ly list_del(&cmd->list); 192025e78531SBryant G. Ly ibmvscsis_free_cmd_resources(vscsi, 192125e78531SBryant G. Ly cmd); 192275dbf2d3SBryant G. Ly /* 192375dbf2d3SBryant G. Ly * With a successfully aborted op 192475dbf2d3SBryant G. Ly * through LIO we want to increment the 192575dbf2d3SBryant G. Ly * the vscsi credit so that when we dont 192675dbf2d3SBryant G. Ly * send a rsp to the original scsi abort 192775dbf2d3SBryant G. Ly * op (h_send_crq), but the tm rsp to 192875dbf2d3SBryant G. Ly * the abort is sent, the credit is 192975dbf2d3SBryant G. Ly * correctly sent with the abort tm rsp. 193075dbf2d3SBryant G. Ly * We would need 1 for the abort tm rsp 193175dbf2d3SBryant G. Ly * and 1 credit for the aborted scsi op. 193275dbf2d3SBryant G. Ly * Thus we need to increment here. 193375dbf2d3SBryant G. Ly * Also we want to increment the credit 193475dbf2d3SBryant G. Ly * here because we want to make sure 193575dbf2d3SBryant G. Ly * cmd is actually released first 193675dbf2d3SBryant G. Ly * otherwise the client will think it 193775dbf2d3SBryant G. Ly * it can send a new cmd, and we could 193875dbf2d3SBryant G. Ly * find ourselves short of cmd elements. 193975dbf2d3SBryant G. Ly */ 194075dbf2d3SBryant G. Ly vscsi->credit += 1; 194125e78531SBryant G. Ly } else { 194288a678bbSBryant G. Ly iue = cmd->iue; 194388a678bbSBryant G. Ly 194488a678bbSBryant G. Ly crq->valid = VALID_CMD_RESP_EL; 194588a678bbSBryant G. Ly crq->format = cmd->rsp.format; 194688a678bbSBryant G. Ly 194788a678bbSBryant G. Ly if (cmd->flags & CMD_FAST_FAIL) 194888a678bbSBryant G. Ly crq->status = VIOSRP_ADAPTER_FAIL; 194988a678bbSBryant G. Ly 195088a678bbSBryant G. Ly crq->IU_length = cpu_to_be16(cmd->rsp.len); 195188a678bbSBryant G. Ly 195288a678bbSBryant G. Ly rc = h_send_crq(vscsi->dma_dev->unit_address, 195388a678bbSBryant G. Ly be64_to_cpu(msg_hi), 195488a678bbSBryant G. Ly be64_to_cpu(cmd->rsp.tag)); 195588a678bbSBryant G. Ly 1956417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "send_messages: cmd %p, tag 0x%llx, rc %ld\n", 1957417dff6cSBryant G. Ly cmd, be64_to_cpu(cmd->rsp.tag), 1958417dff6cSBryant G. Ly rc); 195988a678bbSBryant G. Ly 196025e78531SBryant G. Ly /* if all ok free up the command 196125e78531SBryant G. Ly * element resources 196225e78531SBryant G. Ly */ 196388a678bbSBryant G. Ly if (rc == H_SUCCESS) { 196488a678bbSBryant G. Ly /* some movement has occurred */ 196588a678bbSBryant G. Ly vscsi->rsp_q_timer.timer_pops = 0; 196688a678bbSBryant G. Ly list_del(&cmd->list); 196788a678bbSBryant G. Ly 196825e78531SBryant G. Ly ibmvscsis_free_cmd_resources(vscsi, 196925e78531SBryant G. Ly cmd); 197088a678bbSBryant G. Ly } else { 197188a678bbSBryant G. Ly srp_snd_msg_failed(vscsi, rc); 197288a678bbSBryant G. Ly break; 197388a678bbSBryant G. Ly } 197488a678bbSBryant G. Ly } 197525e78531SBryant G. Ly } 197625e78531SBryant G. Ly } while (retry); 197788a678bbSBryant G. Ly 197888a678bbSBryant G. Ly if (!rc) { 197988a678bbSBryant G. Ly /* 198088a678bbSBryant G. Ly * The timer could pop with the queue empty. If 198188a678bbSBryant G. Ly * this happens, rc will always indicate a 198288a678bbSBryant G. Ly * success; clear the pop count. 198388a678bbSBryant G. Ly */ 198488a678bbSBryant G. Ly vscsi->rsp_q_timer.timer_pops = 0; 198588a678bbSBryant G. Ly } 198688a678bbSBryant G. Ly } else { 198788a678bbSBryant G. Ly ibmvscsis_free_cmd_qs(vscsi); 198888a678bbSBryant G. Ly } 198988a678bbSBryant G. Ly } 199088a678bbSBryant G. Ly 199188a678bbSBryant G. Ly /* Called with intr lock held */ 199288a678bbSBryant G. Ly static void ibmvscsis_send_mad_resp(struct scsi_info *vscsi, 199388a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd, 199488a678bbSBryant G. Ly struct viosrp_crq *crq) 199588a678bbSBryant G. Ly { 199688a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 199788a678bbSBryant G. Ly struct mad_common *mad = (struct mad_common *)&vio_iu(iue)->mad; 199888a678bbSBryant G. Ly uint flag_bits = 0; 199988a678bbSBryant G. Ly long rc; 200088a678bbSBryant G. Ly 200188a678bbSBryant G. Ly dma_wmb(); 200288a678bbSBryant G. Ly rc = h_copy_rdma(sizeof(struct mad_common), 200388a678bbSBryant G. Ly vscsi->dds.window[LOCAL].liobn, iue->sbuf->dma, 200488a678bbSBryant G. Ly vscsi->dds.window[REMOTE].liobn, 200588a678bbSBryant G. Ly be64_to_cpu(crq->IU_data_ptr)); 200688a678bbSBryant G. Ly if (!rc) { 200788a678bbSBryant G. Ly cmd->rsp.format = VIOSRP_MAD_FORMAT; 200888a678bbSBryant G. Ly cmd->rsp.len = sizeof(struct mad_common); 200988a678bbSBryant G. Ly cmd->rsp.tag = mad->tag; 201088a678bbSBryant G. Ly list_add_tail(&cmd->list, &vscsi->waiting_rsp); 201188a678bbSBryant G. Ly ibmvscsis_send_messages(vscsi); 201288a678bbSBryant G. Ly } else { 2013417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Error sending mad response, rc %ld\n", 2014417dff6cSBryant G. Ly rc); 201588a678bbSBryant G. Ly if (rc == H_PERMISSION) { 201688a678bbSBryant G. Ly if (connection_broken(vscsi)) 201788a678bbSBryant G. Ly flag_bits = (RESPONSE_Q_DOWN | CLIENT_FAILED); 201888a678bbSBryant G. Ly } 201988a678bbSBryant G. Ly dev_err(&vscsi->dev, "mad: failed to copy to client, rc %ld\n", 202088a678bbSBryant G. Ly rc); 202188a678bbSBryant G. Ly 202288a678bbSBryant G. Ly ibmvscsis_free_cmd_resources(vscsi, cmd); 202388a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 202488a678bbSBryant G. Ly flag_bits); 202588a678bbSBryant G. Ly } 202688a678bbSBryant G. Ly } 202788a678bbSBryant G. Ly 202888a678bbSBryant G. Ly /** 202988a678bbSBryant G. Ly * ibmvscsis_mad() - Service a MAnagement Data gram. 203088a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 203188a678bbSBryant G. Ly * @crq: Pointer to the CRQ entry containing the MAD request 203288a678bbSBryant G. Ly * 203388a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 203479fac9c9SMichael Cyr * Interrupt, called with adapter lock held 203588a678bbSBryant G. Ly */ 203688a678bbSBryant G. Ly static long ibmvscsis_mad(struct scsi_info *vscsi, struct viosrp_crq *crq) 203788a678bbSBryant G. Ly { 203888a678bbSBryant G. Ly struct iu_entry *iue; 203988a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd; 204088a678bbSBryant G. Ly struct mad_common *mad; 204188a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 204288a678bbSBryant G. Ly 204388a678bbSBryant G. Ly switch (vscsi->state) { 204488a678bbSBryant G. Ly /* 204588a678bbSBryant G. Ly * We have not exchanged Init Msgs yet, so this MAD was sent 204688a678bbSBryant G. Ly * before the last Transport Event; client will not be 204788a678bbSBryant G. Ly * expecting a response. 204888a678bbSBryant G. Ly */ 204988a678bbSBryant G. Ly case WAIT_CONNECTION: 2050417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "mad: in Wait Connection state, ignoring MAD, flags %d\n", 205188a678bbSBryant G. Ly vscsi->flags); 205288a678bbSBryant G. Ly return ADAPT_SUCCESS; 205388a678bbSBryant G. Ly 205488a678bbSBryant G. Ly case SRP_PROCESSING: 205588a678bbSBryant G. Ly case CONNECTED: 205688a678bbSBryant G. Ly break; 205788a678bbSBryant G. Ly 205888a678bbSBryant G. Ly /* 205988a678bbSBryant G. Ly * We should never get here while we're in these states. 206088a678bbSBryant G. Ly * Just log an error and get out. 206188a678bbSBryant G. Ly */ 206288a678bbSBryant G. Ly case UNCONFIGURING: 206388a678bbSBryant G. Ly case WAIT_IDLE: 206488a678bbSBryant G. Ly case ERR_DISCONNECT: 206588a678bbSBryant G. Ly case ERR_DISCONNECT_RECONNECT: 206688a678bbSBryant G. Ly default: 206788a678bbSBryant G. Ly dev_err(&vscsi->dev, "mad: invalid adapter state %d for mad\n", 206888a678bbSBryant G. Ly vscsi->state); 206988a678bbSBryant G. Ly return ADAPT_SUCCESS; 207088a678bbSBryant G. Ly } 207188a678bbSBryant G. Ly 207288a678bbSBryant G. Ly cmd = ibmvscsis_get_free_cmd(vscsi); 207388a678bbSBryant G. Ly if (!cmd) { 207488a678bbSBryant G. Ly dev_err(&vscsi->dev, "mad: failed to get cmd, debit %d\n", 207588a678bbSBryant G. Ly vscsi->debit); 207688a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 207788a678bbSBryant G. Ly return ERROR; 207888a678bbSBryant G. Ly } 207988a678bbSBryant G. Ly iue = cmd->iue; 208088a678bbSBryant G. Ly cmd->type = ADAPTER_MAD; 208188a678bbSBryant G. Ly 208288a678bbSBryant G. Ly rc = ibmvscsis_copy_crq_packet(vscsi, cmd, crq); 208388a678bbSBryant G. Ly if (!rc) { 208488a678bbSBryant G. Ly mad = (struct mad_common *)&vio_iu(iue)->mad; 208588a678bbSBryant G. Ly 2086417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "mad: type %d\n", be32_to_cpu(mad->type)); 208788a678bbSBryant G. Ly 208888a678bbSBryant G. Ly rc = ibmvscsis_process_mad(vscsi, iue); 208988a678bbSBryant G. Ly 2090417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "mad: status %hd, rc %ld\n", 2091417dff6cSBryant G. Ly be16_to_cpu(mad->status), rc); 209288a678bbSBryant G. Ly 209388a678bbSBryant G. Ly if (!rc) 209488a678bbSBryant G. Ly ibmvscsis_send_mad_resp(vscsi, cmd, crq); 209588a678bbSBryant G. Ly } else { 209688a678bbSBryant G. Ly ibmvscsis_free_cmd_resources(vscsi, cmd); 209788a678bbSBryant G. Ly } 209888a678bbSBryant G. Ly 2099417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Leaving mad, rc %ld\n", rc); 210088a678bbSBryant G. Ly return rc; 210188a678bbSBryant G. Ly } 210288a678bbSBryant G. Ly 210388a678bbSBryant G. Ly /** 210488a678bbSBryant G. Ly * ibmvscsis_login_rsp() - Create/copy a login response notice to the client 210588a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 210688a678bbSBryant G. Ly * @cmd: Pointer to the command for the SRP Login request 210788a678bbSBryant G. Ly * 210888a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 210988a678bbSBryant G. Ly * Interrupt, interrupt lock held 211088a678bbSBryant G. Ly */ 211188a678bbSBryant G. Ly static long ibmvscsis_login_rsp(struct scsi_info *vscsi, 211288a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd) 211388a678bbSBryant G. Ly { 211488a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 211588a678bbSBryant G. Ly struct srp_login_rsp *rsp = &vio_iu(iue)->srp.login_rsp; 211688a678bbSBryant G. Ly struct format_code *fmt; 211788a678bbSBryant G. Ly uint flag_bits = 0; 211888a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 211988a678bbSBryant G. Ly 212088a678bbSBryant G. Ly memset(rsp, 0, sizeof(struct srp_login_rsp)); 212188a678bbSBryant G. Ly 212288a678bbSBryant G. Ly rsp->opcode = SRP_LOGIN_RSP; 212388a678bbSBryant G. Ly rsp->req_lim_delta = cpu_to_be32(vscsi->request_limit); 212488a678bbSBryant G. Ly rsp->tag = cmd->rsp.tag; 212588a678bbSBryant G. Ly rsp->max_it_iu_len = cpu_to_be32(SRP_MAX_IU_LEN); 212688a678bbSBryant G. Ly rsp->max_ti_iu_len = cpu_to_be32(SRP_MAX_IU_LEN); 212788a678bbSBryant G. Ly fmt = (struct format_code *)&rsp->buf_fmt; 212888a678bbSBryant G. Ly fmt->buffers = SUPPORTED_FORMATS; 212988a678bbSBryant G. Ly vscsi->credit = 0; 213088a678bbSBryant G. Ly 213188a678bbSBryant G. Ly cmd->rsp.len = sizeof(struct srp_login_rsp); 213288a678bbSBryant G. Ly 213388a678bbSBryant G. Ly dma_wmb(); 213488a678bbSBryant G. Ly rc = h_copy_rdma(cmd->rsp.len, vscsi->dds.window[LOCAL].liobn, 213588a678bbSBryant G. Ly iue->sbuf->dma, vscsi->dds.window[REMOTE].liobn, 213688a678bbSBryant G. Ly be64_to_cpu(iue->remote_token)); 213788a678bbSBryant G. Ly 213888a678bbSBryant G. Ly switch (rc) { 213988a678bbSBryant G. Ly case H_SUCCESS: 214088a678bbSBryant G. Ly break; 214188a678bbSBryant G. Ly 214288a678bbSBryant G. Ly case H_PERMISSION: 214388a678bbSBryant G. Ly if (connection_broken(vscsi)) 214488a678bbSBryant G. Ly flag_bits = RESPONSE_Q_DOWN | CLIENT_FAILED; 214588a678bbSBryant G. Ly dev_err(&vscsi->dev, "login_rsp: error copying to client, rc %ld\n", 214688a678bbSBryant G. Ly rc); 214788a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 214888a678bbSBryant G. Ly flag_bits); 214988a678bbSBryant G. Ly break; 215088a678bbSBryant G. Ly case H_SOURCE_PARM: 215188a678bbSBryant G. Ly case H_DEST_PARM: 215288a678bbSBryant G. Ly default: 215388a678bbSBryant G. Ly dev_err(&vscsi->dev, "login_rsp: error copying to client, rc %ld\n", 215488a678bbSBryant G. Ly rc); 215588a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 215688a678bbSBryant G. Ly break; 215788a678bbSBryant G. Ly } 215888a678bbSBryant G. Ly 215988a678bbSBryant G. Ly return rc; 216088a678bbSBryant G. Ly } 216188a678bbSBryant G. Ly 216288a678bbSBryant G. Ly /** 216388a678bbSBryant G. Ly * ibmvscsis_srp_login_rej() - Create/copy a login rejection notice to client 216488a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 216588a678bbSBryant G. Ly * @cmd: Pointer to the command for the SRP Login request 216688a678bbSBryant G. Ly * @reason: The reason the SRP Login is being rejected, per SRP protocol 216788a678bbSBryant G. Ly * 216888a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 216988a678bbSBryant G. Ly * Interrupt, interrupt lock held 217088a678bbSBryant G. Ly */ 217188a678bbSBryant G. Ly static long ibmvscsis_srp_login_rej(struct scsi_info *vscsi, 217288a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd, u32 reason) 217388a678bbSBryant G. Ly { 217488a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 217588a678bbSBryant G. Ly struct srp_login_rej *rej = &vio_iu(iue)->srp.login_rej; 217688a678bbSBryant G. Ly struct format_code *fmt; 217788a678bbSBryant G. Ly uint flag_bits = 0; 217888a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 217988a678bbSBryant G. Ly 218088a678bbSBryant G. Ly memset(rej, 0, sizeof(*rej)); 218188a678bbSBryant G. Ly 218288a678bbSBryant G. Ly rej->opcode = SRP_LOGIN_REJ; 218388a678bbSBryant G. Ly rej->reason = cpu_to_be32(reason); 218488a678bbSBryant G. Ly rej->tag = cmd->rsp.tag; 218588a678bbSBryant G. Ly fmt = (struct format_code *)&rej->buf_fmt; 218688a678bbSBryant G. Ly fmt->buffers = SUPPORTED_FORMATS; 218788a678bbSBryant G. Ly 218888a678bbSBryant G. Ly cmd->rsp.len = sizeof(*rej); 218988a678bbSBryant G. Ly 219088a678bbSBryant G. Ly dma_wmb(); 219188a678bbSBryant G. Ly rc = h_copy_rdma(cmd->rsp.len, vscsi->dds.window[LOCAL].liobn, 219288a678bbSBryant G. Ly iue->sbuf->dma, vscsi->dds.window[REMOTE].liobn, 219388a678bbSBryant G. Ly be64_to_cpu(iue->remote_token)); 219488a678bbSBryant G. Ly 219588a678bbSBryant G. Ly switch (rc) { 219688a678bbSBryant G. Ly case H_SUCCESS: 219788a678bbSBryant G. Ly break; 219888a678bbSBryant G. Ly case H_PERMISSION: 219988a678bbSBryant G. Ly if (connection_broken(vscsi)) 220088a678bbSBryant G. Ly flag_bits = RESPONSE_Q_DOWN | CLIENT_FAILED; 220188a678bbSBryant G. Ly dev_err(&vscsi->dev, "login_rej: error copying to client, rc %ld\n", 220288a678bbSBryant G. Ly rc); 220388a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 220488a678bbSBryant G. Ly flag_bits); 220588a678bbSBryant G. Ly break; 220688a678bbSBryant G. Ly case H_SOURCE_PARM: 220788a678bbSBryant G. Ly case H_DEST_PARM: 220888a678bbSBryant G. Ly default: 220988a678bbSBryant G. Ly dev_err(&vscsi->dev, "login_rej: error copying to client, rc %ld\n", 221088a678bbSBryant G. Ly rc); 221188a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 221288a678bbSBryant G. Ly break; 221388a678bbSBryant G. Ly } 221488a678bbSBryant G. Ly 221588a678bbSBryant G. Ly return rc; 221688a678bbSBryant G. Ly } 221788a678bbSBryant G. Ly 221888a678bbSBryant G. Ly static int ibmvscsis_make_nexus(struct ibmvscsis_tport *tport) 221988a678bbSBryant G. Ly { 222088a678bbSBryant G. Ly char *name = tport->tport_name; 222188a678bbSBryant G. Ly struct ibmvscsis_nexus *nexus; 2222417dff6cSBryant G. Ly struct scsi_info *vscsi = container_of(tport, struct scsi_info, tport); 222388a678bbSBryant G. Ly int rc; 222488a678bbSBryant G. Ly 222588a678bbSBryant G. Ly if (tport->ibmv_nexus) { 2226417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "tport->ibmv_nexus already exists\n"); 222788a678bbSBryant G. Ly return 0; 222888a678bbSBryant G. Ly } 222988a678bbSBryant G. Ly 223088a678bbSBryant G. Ly nexus = kzalloc(sizeof(*nexus), GFP_KERNEL); 223188a678bbSBryant G. Ly if (!nexus) { 2232417dff6cSBryant G. Ly dev_err(&vscsi->dev, "Unable to allocate struct ibmvscsis_nexus\n"); 223388a678bbSBryant G. Ly return -ENOMEM; 223488a678bbSBryant G. Ly } 223588a678bbSBryant G. Ly 2236*fa834287SMike Christie nexus->se_sess = target_setup_session(&tport->se_tpg, 0, 0, 223788a678bbSBryant G. Ly TARGET_PROT_NORMAL, name, nexus, 223888a678bbSBryant G. Ly NULL); 223988a678bbSBryant G. Ly if (IS_ERR(nexus->se_sess)) { 224088a678bbSBryant G. Ly rc = PTR_ERR(nexus->se_sess); 224188a678bbSBryant G. Ly goto transport_init_fail; 224288a678bbSBryant G. Ly } 224388a678bbSBryant G. Ly 224488a678bbSBryant G. Ly tport->ibmv_nexus = nexus; 224588a678bbSBryant G. Ly 224688a678bbSBryant G. Ly return 0; 224788a678bbSBryant G. Ly 224888a678bbSBryant G. Ly transport_init_fail: 224988a678bbSBryant G. Ly kfree(nexus); 225088a678bbSBryant G. Ly return rc; 225188a678bbSBryant G. Ly } 225288a678bbSBryant G. Ly 225388a678bbSBryant G. Ly static int ibmvscsis_drop_nexus(struct ibmvscsis_tport *tport) 225488a678bbSBryant G. Ly { 225588a678bbSBryant G. Ly struct se_session *se_sess; 225688a678bbSBryant G. Ly struct ibmvscsis_nexus *nexus; 225788a678bbSBryant G. Ly 225888a678bbSBryant G. Ly nexus = tport->ibmv_nexus; 225988a678bbSBryant G. Ly if (!nexus) 226088a678bbSBryant G. Ly return -ENODEV; 226188a678bbSBryant G. Ly 226288a678bbSBryant G. Ly se_sess = nexus->se_sess; 226388a678bbSBryant G. Ly if (!se_sess) 226488a678bbSBryant G. Ly return -ENODEV; 226588a678bbSBryant G. Ly 226688a678bbSBryant G. Ly /* 226788a678bbSBryant G. Ly * Release the SCSI I_T Nexus to the emulated ibmvscsis Target Port 226888a678bbSBryant G. Ly */ 2269712db3ebSBryant G. Ly target_wait_for_sess_cmds(se_sess); 2270712db3ebSBryant G. Ly transport_deregister_session_configfs(se_sess); 227188a678bbSBryant G. Ly transport_deregister_session(se_sess); 227288a678bbSBryant G. Ly tport->ibmv_nexus = NULL; 227388a678bbSBryant G. Ly kfree(nexus); 227488a678bbSBryant G. Ly 227588a678bbSBryant G. Ly return 0; 227688a678bbSBryant G. Ly } 227788a678bbSBryant G. Ly 227888a678bbSBryant G. Ly /** 227988a678bbSBryant G. Ly * ibmvscsis_srp_login() - Process an SRP Login Request 228088a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 228188a678bbSBryant G. Ly * @cmd: Command element to use to process the SRP Login request 228288a678bbSBryant G. Ly * @crq: Pointer to CRQ entry containing the SRP Login request 228388a678bbSBryant G. Ly * 228488a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 228588a678bbSBryant G. Ly * Interrupt, called with interrupt lock held 228688a678bbSBryant G. Ly */ 228788a678bbSBryant G. Ly static long ibmvscsis_srp_login(struct scsi_info *vscsi, 228888a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd, 228988a678bbSBryant G. Ly struct viosrp_crq *crq) 229088a678bbSBryant G. Ly { 229188a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 229288a678bbSBryant G. Ly struct srp_login_req *req = &vio_iu(iue)->srp.login_req; 229388a678bbSBryant G. Ly struct port_id { 229488a678bbSBryant G. Ly __be64 id_extension; 229588a678bbSBryant G. Ly __be64 io_guid; 229688a678bbSBryant G. Ly } *iport, *tport; 229788a678bbSBryant G. Ly struct format_code *fmt; 229888a678bbSBryant G. Ly u32 reason = 0x0; 229988a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 230088a678bbSBryant G. Ly 230188a678bbSBryant G. Ly iport = (struct port_id *)req->initiator_port_id; 230288a678bbSBryant G. Ly tport = (struct port_id *)req->target_port_id; 230388a678bbSBryant G. Ly fmt = (struct format_code *)&req->req_buf_fmt; 230488a678bbSBryant G. Ly if (be32_to_cpu(req->req_it_iu_len) > SRP_MAX_IU_LEN) 230588a678bbSBryant G. Ly reason = SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE; 230688a678bbSBryant G. Ly else if (be32_to_cpu(req->req_it_iu_len) < 64) 230788a678bbSBryant G. Ly reason = SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL; 230888a678bbSBryant G. Ly else if ((be64_to_cpu(iport->id_extension) > (MAX_NUM_PORTS - 1)) || 230988a678bbSBryant G. Ly (be64_to_cpu(tport->id_extension) > (MAX_NUM_PORTS - 1))) 231088a678bbSBryant G. Ly reason = SRP_LOGIN_REJ_UNABLE_ASSOCIATE_CHANNEL; 231188a678bbSBryant G. Ly else if (req->req_flags & SRP_MULTICHAN_MULTI) 231288a678bbSBryant G. Ly reason = SRP_LOGIN_REJ_MULTI_CHANNEL_UNSUPPORTED; 231388a678bbSBryant G. Ly else if (fmt->buffers & (~SUPPORTED_FORMATS)) 231488a678bbSBryant G. Ly reason = SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT; 2315f6dbe38eSBryant G. Ly else if ((fmt->buffers & SUPPORTED_FORMATS) == 0) 231688a678bbSBryant G. Ly reason = SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT; 231788a678bbSBryant G. Ly 231888a678bbSBryant G. Ly if (vscsi->state == SRP_PROCESSING) 231988a678bbSBryant G. Ly reason = SRP_LOGIN_REJ_CHANNEL_LIMIT_REACHED; 232088a678bbSBryant G. Ly 232188a678bbSBryant G. Ly rc = ibmvscsis_make_nexus(&vscsi->tport); 232288a678bbSBryant G. Ly if (rc) 232388a678bbSBryant G. Ly reason = SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL; 232488a678bbSBryant G. Ly 232588a678bbSBryant G. Ly cmd->rsp.format = VIOSRP_SRP_FORMAT; 232688a678bbSBryant G. Ly cmd->rsp.tag = req->tag; 232788a678bbSBryant G. Ly 2328417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "srp_login: reason 0x%x\n", reason); 232988a678bbSBryant G. Ly 233088a678bbSBryant G. Ly if (reason) 233188a678bbSBryant G. Ly rc = ibmvscsis_srp_login_rej(vscsi, cmd, reason); 233288a678bbSBryant G. Ly else 233388a678bbSBryant G. Ly rc = ibmvscsis_login_rsp(vscsi, cmd); 233488a678bbSBryant G. Ly 233588a678bbSBryant G. Ly if (!rc) { 233688a678bbSBryant G. Ly if (!reason) 233788a678bbSBryant G. Ly vscsi->state = SRP_PROCESSING; 233888a678bbSBryant G. Ly 233988a678bbSBryant G. Ly list_add_tail(&cmd->list, &vscsi->waiting_rsp); 234088a678bbSBryant G. Ly ibmvscsis_send_messages(vscsi); 234188a678bbSBryant G. Ly } else { 234288a678bbSBryant G. Ly ibmvscsis_free_cmd_resources(vscsi, cmd); 234388a678bbSBryant G. Ly } 234488a678bbSBryant G. Ly 2345417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Leaving srp_login, rc %ld\n", rc); 234688a678bbSBryant G. Ly return rc; 234788a678bbSBryant G. Ly } 234888a678bbSBryant G. Ly 234988a678bbSBryant G. Ly /** 235088a678bbSBryant G. Ly * ibmvscsis_srp_i_logout() - Helper Function to close I_T Nexus 235188a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 235288a678bbSBryant G. Ly * @cmd: Command element to use to process the Implicit Logout request 235388a678bbSBryant G. Ly * @crq: Pointer to CRQ entry containing the Implicit Logout request 235488a678bbSBryant G. Ly * 235588a678bbSBryant G. Ly * Do the logic to close the I_T nexus. This function may not 235688a678bbSBryant G. Ly * behave to specification. 235788a678bbSBryant G. Ly * 235888a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 235988a678bbSBryant G. Ly * Interrupt, interrupt lock held 236088a678bbSBryant G. Ly */ 236188a678bbSBryant G. Ly static long ibmvscsis_srp_i_logout(struct scsi_info *vscsi, 236288a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd, 236388a678bbSBryant G. Ly struct viosrp_crq *crq) 236488a678bbSBryant G. Ly { 236588a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 236688a678bbSBryant G. Ly struct srp_i_logout *log_out = &vio_iu(iue)->srp.i_logout; 236788a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 236888a678bbSBryant G. Ly 236988a678bbSBryant G. Ly if ((vscsi->debit > 0) || !list_empty(&vscsi->schedule_q) || 237088a678bbSBryant G. Ly !list_empty(&vscsi->waiting_rsp)) { 237188a678bbSBryant G. Ly dev_err(&vscsi->dev, "i_logout: outstanding work\n"); 237288a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0); 237388a678bbSBryant G. Ly } else { 237488a678bbSBryant G. Ly cmd->rsp.format = SRP_FORMAT; 237588a678bbSBryant G. Ly cmd->rsp.tag = log_out->tag; 237688a678bbSBryant G. Ly cmd->rsp.len = sizeof(struct mad_common); 237788a678bbSBryant G. Ly list_add_tail(&cmd->list, &vscsi->waiting_rsp); 237888a678bbSBryant G. Ly ibmvscsis_send_messages(vscsi); 237988a678bbSBryant G. Ly 238088a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 0); 238188a678bbSBryant G. Ly } 238288a678bbSBryant G. Ly 238388a678bbSBryant G. Ly return rc; 238488a678bbSBryant G. Ly } 238588a678bbSBryant G. Ly 238688a678bbSBryant G. Ly /* Called with intr lock held */ 238788a678bbSBryant G. Ly static void ibmvscsis_srp_cmd(struct scsi_info *vscsi, struct viosrp_crq *crq) 238888a678bbSBryant G. Ly { 238988a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd; 239088a678bbSBryant G. Ly struct iu_entry *iue; 239188a678bbSBryant G. Ly struct srp_cmd *srp; 239288a678bbSBryant G. Ly struct srp_tsk_mgmt *tsk; 239388a678bbSBryant G. Ly long rc; 239488a678bbSBryant G. Ly 239588a678bbSBryant G. Ly if (vscsi->request_limit - vscsi->debit <= 0) { 239688a678bbSBryant G. Ly /* Client has exceeded request limit */ 239788a678bbSBryant G. Ly dev_err(&vscsi->dev, "Client exceeded the request limit (%d), debit %d\n", 239888a678bbSBryant G. Ly vscsi->request_limit, vscsi->debit); 239988a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 240088a678bbSBryant G. Ly return; 240188a678bbSBryant G. Ly } 240288a678bbSBryant G. Ly 240388a678bbSBryant G. Ly cmd = ibmvscsis_get_free_cmd(vscsi); 240488a678bbSBryant G. Ly if (!cmd) { 240588a678bbSBryant G. Ly dev_err(&vscsi->dev, "srp_cmd failed to get cmd, debit %d\n", 240688a678bbSBryant G. Ly vscsi->debit); 240788a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 240888a678bbSBryant G. Ly return; 240988a678bbSBryant G. Ly } 241088a678bbSBryant G. Ly iue = cmd->iue; 241188a678bbSBryant G. Ly srp = &vio_iu(iue)->srp.cmd; 241288a678bbSBryant G. Ly 241388a678bbSBryant G. Ly rc = ibmvscsis_copy_crq_packet(vscsi, cmd, crq); 241488a678bbSBryant G. Ly if (rc) { 241588a678bbSBryant G. Ly ibmvscsis_free_cmd_resources(vscsi, cmd); 241688a678bbSBryant G. Ly return; 241788a678bbSBryant G. Ly } 241888a678bbSBryant G. Ly 241988a678bbSBryant G. Ly if (vscsi->state == SRP_PROCESSING) { 242088a678bbSBryant G. Ly switch (srp->opcode) { 242188a678bbSBryant G. Ly case SRP_LOGIN_REQ: 242288a678bbSBryant G. Ly rc = ibmvscsis_srp_login(vscsi, cmd, crq); 242388a678bbSBryant G. Ly break; 242488a678bbSBryant G. Ly 242588a678bbSBryant G. Ly case SRP_TSK_MGMT: 242688a678bbSBryant G. Ly tsk = &vio_iu(iue)->srp.tsk_mgmt; 2427417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "tsk_mgmt tag: %llu (0x%llx)\n", 2428417dff6cSBryant G. Ly tsk->tag, tsk->tag); 242988a678bbSBryant G. Ly cmd->rsp.tag = tsk->tag; 243088a678bbSBryant G. Ly vscsi->debit += 1; 243188a678bbSBryant G. Ly cmd->type = TASK_MANAGEMENT; 243288a678bbSBryant G. Ly list_add_tail(&cmd->list, &vscsi->schedule_q); 243388a678bbSBryant G. Ly queue_work(vscsi->work_q, &cmd->work); 243488a678bbSBryant G. Ly break; 243588a678bbSBryant G. Ly 243688a678bbSBryant G. Ly case SRP_CMD: 2437417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "srp_cmd tag: %llu (0x%llx)\n", 2438417dff6cSBryant G. Ly srp->tag, srp->tag); 243988a678bbSBryant G. Ly cmd->rsp.tag = srp->tag; 244088a678bbSBryant G. Ly vscsi->debit += 1; 244188a678bbSBryant G. Ly cmd->type = SCSI_CDB; 244288a678bbSBryant G. Ly /* 244388a678bbSBryant G. Ly * We want to keep track of work waiting for 244488a678bbSBryant G. Ly * the workqueue. 244588a678bbSBryant G. Ly */ 244688a678bbSBryant G. Ly list_add_tail(&cmd->list, &vscsi->schedule_q); 244788a678bbSBryant G. Ly queue_work(vscsi->work_q, &cmd->work); 244888a678bbSBryant G. Ly break; 244988a678bbSBryant G. Ly 245088a678bbSBryant G. Ly case SRP_I_LOGOUT: 245188a678bbSBryant G. Ly rc = ibmvscsis_srp_i_logout(vscsi, cmd, crq); 245288a678bbSBryant G. Ly break; 245388a678bbSBryant G. Ly 245488a678bbSBryant G. Ly case SRP_CRED_RSP: 245588a678bbSBryant G. Ly case SRP_AER_RSP: 245688a678bbSBryant G. Ly default: 245788a678bbSBryant G. Ly ibmvscsis_free_cmd_resources(vscsi, cmd); 245888a678bbSBryant G. Ly dev_err(&vscsi->dev, "invalid srp cmd, opcode %d\n", 245988a678bbSBryant G. Ly (uint)srp->opcode); 246088a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, 246188a678bbSBryant G. Ly ERR_DISCONNECT_RECONNECT, 0); 246288a678bbSBryant G. Ly break; 246388a678bbSBryant G. Ly } 246488a678bbSBryant G. Ly } else if (srp->opcode == SRP_LOGIN_REQ && vscsi->state == CONNECTED) { 246588a678bbSBryant G. Ly rc = ibmvscsis_srp_login(vscsi, cmd, crq); 246688a678bbSBryant G. Ly } else { 246788a678bbSBryant G. Ly ibmvscsis_free_cmd_resources(vscsi, cmd); 246888a678bbSBryant G. Ly dev_err(&vscsi->dev, "Invalid state %d to handle srp cmd\n", 246988a678bbSBryant G. Ly vscsi->state); 247088a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 247188a678bbSBryant G. Ly } 247288a678bbSBryant G. Ly } 247388a678bbSBryant G. Ly 247488a678bbSBryant G. Ly /** 247588a678bbSBryant G. Ly * ibmvscsis_ping_response() - Respond to a ping request 247688a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 247788a678bbSBryant G. Ly * 247888a678bbSBryant G. Ly * Let the client know that the server is alive and waiting on 247988a678bbSBryant G. Ly * its native I/O stack. 248088a678bbSBryant G. Ly * If any type of error occurs from the call to queue a ping 248188a678bbSBryant G. Ly * response then the client is either not accepting or receiving 248288a678bbSBryant G. Ly * interrupts. Disconnect with an error. 248388a678bbSBryant G. Ly * 248488a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 248588a678bbSBryant G. Ly * Interrupt, interrupt lock held 248688a678bbSBryant G. Ly */ 248788a678bbSBryant G. Ly static long ibmvscsis_ping_response(struct scsi_info *vscsi) 248888a678bbSBryant G. Ly { 248988a678bbSBryant G. Ly struct viosrp_crq *crq; 249088a678bbSBryant G. Ly u64 buffer[2] = { 0, 0 }; 249188a678bbSBryant G. Ly long rc; 249288a678bbSBryant G. Ly 249388a678bbSBryant G. Ly crq = (struct viosrp_crq *)&buffer; 249488a678bbSBryant G. Ly crq->valid = VALID_CMD_RESP_EL; 249588a678bbSBryant G. Ly crq->format = (u8)MESSAGE_IN_CRQ; 249688a678bbSBryant G. Ly crq->status = PING_RESPONSE; 249788a678bbSBryant G. Ly 249888a678bbSBryant G. Ly rc = h_send_crq(vscsi->dds.unit_id, cpu_to_be64(buffer[MSG_HI]), 249988a678bbSBryant G. Ly cpu_to_be64(buffer[MSG_LOW])); 250088a678bbSBryant G. Ly 250188a678bbSBryant G. Ly switch (rc) { 250288a678bbSBryant G. Ly case H_SUCCESS: 250388a678bbSBryant G. Ly break; 250488a678bbSBryant G. Ly case H_CLOSED: 250588a678bbSBryant G. Ly vscsi->flags |= CLIENT_FAILED; 250688a678bbSBryant G. Ly case H_DROPPED: 250788a678bbSBryant G. Ly vscsi->flags |= RESPONSE_Q_DOWN; 250888a678bbSBryant G. Ly case H_REMOTE_PARM: 250988a678bbSBryant G. Ly dev_err(&vscsi->dev, "ping_response: h_send_crq failed, rc %ld\n", 251088a678bbSBryant G. Ly rc); 251188a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 251288a678bbSBryant G. Ly break; 251388a678bbSBryant G. Ly default: 251488a678bbSBryant G. Ly dev_err(&vscsi->dev, "ping_response: h_send_crq returned unknown rc %ld\n", 251588a678bbSBryant G. Ly rc); 251688a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0); 251788a678bbSBryant G. Ly break; 251888a678bbSBryant G. Ly } 251988a678bbSBryant G. Ly 252088a678bbSBryant G. Ly return rc; 252188a678bbSBryant G. Ly } 252288a678bbSBryant G. Ly 252388a678bbSBryant G. Ly /** 252488a678bbSBryant G. Ly * ibmvscsis_parse_command() - Parse an element taken from the cmd rsp queue. 252588a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 252688a678bbSBryant G. Ly * @crq: Pointer to CRQ element containing the SRP request 252788a678bbSBryant G. Ly * 252888a678bbSBryant G. Ly * This function will return success if the command queue element is valid 252988a678bbSBryant G. Ly * and the srp iu or MAD request it pointed to was also valid. That does 253088a678bbSBryant G. Ly * not mean that an error was not returned to the client. 253188a678bbSBryant G. Ly * 253288a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 253388a678bbSBryant G. Ly * Interrupt, intr lock held 253488a678bbSBryant G. Ly */ 253588a678bbSBryant G. Ly static long ibmvscsis_parse_command(struct scsi_info *vscsi, 253688a678bbSBryant G. Ly struct viosrp_crq *crq) 253788a678bbSBryant G. Ly { 253888a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 253988a678bbSBryant G. Ly 254088a678bbSBryant G. Ly switch (crq->valid) { 254188a678bbSBryant G. Ly case VALID_CMD_RESP_EL: 254288a678bbSBryant G. Ly switch (crq->format) { 254388a678bbSBryant G. Ly case OS400_FORMAT: 254488a678bbSBryant G. Ly case AIX_FORMAT: 254588a678bbSBryant G. Ly case LINUX_FORMAT: 254688a678bbSBryant G. Ly case MAD_FORMAT: 254788a678bbSBryant G. Ly if (vscsi->flags & PROCESSING_MAD) { 254888a678bbSBryant G. Ly rc = ERROR; 254988a678bbSBryant G. Ly dev_err(&vscsi->dev, "parse_command: already processing mad\n"); 255088a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, 255188a678bbSBryant G. Ly ERR_DISCONNECT_RECONNECT, 255288a678bbSBryant G. Ly 0); 255388a678bbSBryant G. Ly } else { 255488a678bbSBryant G. Ly vscsi->flags |= PROCESSING_MAD; 255588a678bbSBryant G. Ly rc = ibmvscsis_mad(vscsi, crq); 255688a678bbSBryant G. Ly } 255788a678bbSBryant G. Ly break; 255888a678bbSBryant G. Ly 255988a678bbSBryant G. Ly case SRP_FORMAT: 256088a678bbSBryant G. Ly ibmvscsis_srp_cmd(vscsi, crq); 256188a678bbSBryant G. Ly break; 256288a678bbSBryant G. Ly 256388a678bbSBryant G. Ly case MESSAGE_IN_CRQ: 256488a678bbSBryant G. Ly if (crq->status == PING) 256588a678bbSBryant G. Ly ibmvscsis_ping_response(vscsi); 256688a678bbSBryant G. Ly break; 256788a678bbSBryant G. Ly 256888a678bbSBryant G. Ly default: 256988a678bbSBryant G. Ly dev_err(&vscsi->dev, "parse_command: invalid format %d\n", 257088a678bbSBryant G. Ly (uint)crq->format); 257188a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, 257288a678bbSBryant G. Ly ERR_DISCONNECT_RECONNECT, 0); 257388a678bbSBryant G. Ly break; 257488a678bbSBryant G. Ly } 257588a678bbSBryant G. Ly break; 257688a678bbSBryant G. Ly 257788a678bbSBryant G. Ly case VALID_TRANS_EVENT: 257888a678bbSBryant G. Ly rc = ibmvscsis_trans_event(vscsi, crq); 257988a678bbSBryant G. Ly break; 258088a678bbSBryant G. Ly 258188a678bbSBryant G. Ly case VALID_INIT_MSG: 258288a678bbSBryant G. Ly rc = ibmvscsis_init_msg(vscsi, crq); 258388a678bbSBryant G. Ly break; 258488a678bbSBryant G. Ly 258588a678bbSBryant G. Ly default: 258688a678bbSBryant G. Ly dev_err(&vscsi->dev, "parse_command: invalid valid field %d\n", 258788a678bbSBryant G. Ly (uint)crq->valid); 258888a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 258988a678bbSBryant G. Ly break; 259088a678bbSBryant G. Ly } 259188a678bbSBryant G. Ly 259288a678bbSBryant G. Ly /* 259388a678bbSBryant G. Ly * Return only what the interrupt handler cares 259488a678bbSBryant G. Ly * about. Most errors we keep right on trucking. 259588a678bbSBryant G. Ly */ 259688a678bbSBryant G. Ly rc = vscsi->flags & SCHEDULE_DISCONNECT; 259788a678bbSBryant G. Ly 259888a678bbSBryant G. Ly return rc; 259988a678bbSBryant G. Ly } 260088a678bbSBryant G. Ly 260188a678bbSBryant G. Ly static int read_dma_window(struct scsi_info *vscsi) 260288a678bbSBryant G. Ly { 260388a678bbSBryant G. Ly struct vio_dev *vdev = vscsi->dma_dev; 260488a678bbSBryant G. Ly const __be32 *dma_window; 260588a678bbSBryant G. Ly const __be32 *prop; 260688a678bbSBryant G. Ly 260788a678bbSBryant G. Ly /* TODO Using of_parse_dma_window would be better, but it doesn't give 260888a678bbSBryant G. Ly * a way to read multiple windows without already knowing the size of 260988a678bbSBryant G. Ly * a window or the number of windows. 261088a678bbSBryant G. Ly */ 261188a678bbSBryant G. Ly dma_window = (const __be32 *)vio_get_attribute(vdev, 261288a678bbSBryant G. Ly "ibm,my-dma-window", 261388a678bbSBryant G. Ly NULL); 261488a678bbSBryant G. Ly if (!dma_window) { 2615417dff6cSBryant G. Ly dev_err(&vscsi->dev, "Couldn't find ibm,my-dma-window property\n"); 261688a678bbSBryant G. Ly return -1; 261788a678bbSBryant G. Ly } 261888a678bbSBryant G. Ly 261988a678bbSBryant G. Ly vscsi->dds.window[LOCAL].liobn = be32_to_cpu(*dma_window); 262088a678bbSBryant G. Ly dma_window++; 262188a678bbSBryant G. Ly 262288a678bbSBryant G. Ly prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-address-cells", 262388a678bbSBryant G. Ly NULL); 262488a678bbSBryant G. Ly if (!prop) { 2625417dff6cSBryant G. Ly dev_warn(&vscsi->dev, "Couldn't find ibm,#dma-address-cells property\n"); 262688a678bbSBryant G. Ly dma_window++; 262788a678bbSBryant G. Ly } else { 262888a678bbSBryant G. Ly dma_window += be32_to_cpu(*prop); 262988a678bbSBryant G. Ly } 263088a678bbSBryant G. Ly 263188a678bbSBryant G. Ly prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-size-cells", 263288a678bbSBryant G. Ly NULL); 263388a678bbSBryant G. Ly if (!prop) { 2634417dff6cSBryant G. Ly dev_warn(&vscsi->dev, "Couldn't find ibm,#dma-size-cells property\n"); 263588a678bbSBryant G. Ly dma_window++; 263688a678bbSBryant G. Ly } else { 263788a678bbSBryant G. Ly dma_window += be32_to_cpu(*prop); 263888a678bbSBryant G. Ly } 263988a678bbSBryant G. Ly 264088a678bbSBryant G. Ly /* dma_window should point to the second window now */ 264188a678bbSBryant G. Ly vscsi->dds.window[REMOTE].liobn = be32_to_cpu(*dma_window); 264288a678bbSBryant G. Ly 264388a678bbSBryant G. Ly return 0; 264488a678bbSBryant G. Ly } 264588a678bbSBryant G. Ly 264688a678bbSBryant G. Ly static struct ibmvscsis_tport *ibmvscsis_lookup_port(const char *name) 264788a678bbSBryant G. Ly { 264888a678bbSBryant G. Ly struct ibmvscsis_tport *tport = NULL; 264988a678bbSBryant G. Ly struct vio_dev *vdev; 265088a678bbSBryant G. Ly struct scsi_info *vscsi; 265188a678bbSBryant G. Ly 265288a678bbSBryant G. Ly spin_lock_bh(&ibmvscsis_dev_lock); 265388a678bbSBryant G. Ly list_for_each_entry(vscsi, &ibmvscsis_dev_list, list) { 265488a678bbSBryant G. Ly vdev = vscsi->dma_dev; 265588a678bbSBryant G. Ly if (!strcmp(dev_name(&vdev->dev), name)) { 265688a678bbSBryant G. Ly tport = &vscsi->tport; 265788a678bbSBryant G. Ly break; 265888a678bbSBryant G. Ly } 265988a678bbSBryant G. Ly } 266088a678bbSBryant G. Ly spin_unlock_bh(&ibmvscsis_dev_lock); 266188a678bbSBryant G. Ly 266288a678bbSBryant G. Ly return tport; 266388a678bbSBryant G. Ly } 266488a678bbSBryant G. Ly 266588a678bbSBryant G. Ly /** 266688a678bbSBryant G. Ly * ibmvscsis_parse_cmd() - Parse SRP Command 266788a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 266888a678bbSBryant G. Ly * @cmd: Pointer to command element with SRP command 266988a678bbSBryant G. Ly * 267088a678bbSBryant G. Ly * Parse the srp command; if it is valid then submit it to tcm. 267188a678bbSBryant G. Ly * Note: The return code does not reflect the status of the SCSI CDB. 267288a678bbSBryant G. Ly * 267388a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 267488a678bbSBryant G. Ly * Process level 267588a678bbSBryant G. Ly */ 267688a678bbSBryant G. Ly static void ibmvscsis_parse_cmd(struct scsi_info *vscsi, 267788a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd) 267888a678bbSBryant G. Ly { 267988a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 268088a678bbSBryant G. Ly struct srp_cmd *srp = (struct srp_cmd *)iue->sbuf->buf; 268188a678bbSBryant G. Ly struct ibmvscsis_nexus *nexus; 268288a678bbSBryant G. Ly u64 data_len = 0; 268388a678bbSBryant G. Ly enum dma_data_direction dir; 268488a678bbSBryant G. Ly int attr = 0; 268588a678bbSBryant G. Ly int rc = 0; 268688a678bbSBryant G. Ly 268788a678bbSBryant G. Ly nexus = vscsi->tport.ibmv_nexus; 268888a678bbSBryant G. Ly /* 268988a678bbSBryant G. Ly * additional length in bytes. Note that the SRP spec says that 269088a678bbSBryant G. Ly * additional length is in 4-byte words, but technically the 269188a678bbSBryant G. Ly * additional length field is only the upper 6 bits of the byte. 269288a678bbSBryant G. Ly * The lower 2 bits are reserved. If the lower 2 bits are 0 (as 269388a678bbSBryant G. Ly * all reserved fields should be), then interpreting the byte as 269488a678bbSBryant G. Ly * an int will yield the length in bytes. 269588a678bbSBryant G. Ly */ 269688a678bbSBryant G. Ly if (srp->add_cdb_len & 0x03) { 269788a678bbSBryant G. Ly dev_err(&vscsi->dev, "parse_cmd: reserved bits set in IU\n"); 269888a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 269988a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 270088a678bbSBryant G. Ly ibmvscsis_free_cmd_resources(vscsi, cmd); 270188a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 270288a678bbSBryant G. Ly return; 270388a678bbSBryant G. Ly } 270488a678bbSBryant G. Ly 270588a678bbSBryant G. Ly if (srp_get_desc_table(srp, &dir, &data_len)) { 270688a678bbSBryant G. Ly dev_err(&vscsi->dev, "0x%llx: parsing SRP descriptor table failed.\n", 270788a678bbSBryant G. Ly srp->tag); 270888a678bbSBryant G. Ly goto fail; 270988a678bbSBryant G. Ly } 271088a678bbSBryant G. Ly 271188a678bbSBryant G. Ly cmd->rsp.sol_not = srp->sol_not; 271288a678bbSBryant G. Ly 271388a678bbSBryant G. Ly switch (srp->task_attr) { 271488a678bbSBryant G. Ly case SRP_SIMPLE_TASK: 271588a678bbSBryant G. Ly attr = TCM_SIMPLE_TAG; 271688a678bbSBryant G. Ly break; 271788a678bbSBryant G. Ly case SRP_ORDERED_TASK: 271888a678bbSBryant G. Ly attr = TCM_ORDERED_TAG; 271988a678bbSBryant G. Ly break; 272088a678bbSBryant G. Ly case SRP_HEAD_TASK: 272188a678bbSBryant G. Ly attr = TCM_HEAD_TAG; 272288a678bbSBryant G. Ly break; 272388a678bbSBryant G. Ly case SRP_ACA_TASK: 272488a678bbSBryant G. Ly attr = TCM_ACA_TAG; 272588a678bbSBryant G. Ly break; 272688a678bbSBryant G. Ly default: 272788a678bbSBryant G. Ly dev_err(&vscsi->dev, "Invalid task attribute %d\n", 272888a678bbSBryant G. Ly srp->task_attr); 272988a678bbSBryant G. Ly goto fail; 273088a678bbSBryant G. Ly } 273188a678bbSBryant G. Ly 273288a678bbSBryant G. Ly cmd->se_cmd.tag = be64_to_cpu(srp->tag); 273388a678bbSBryant G. Ly 273488a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 273588a678bbSBryant G. Ly list_add_tail(&cmd->list, &vscsi->active_q); 273688a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 273788a678bbSBryant G. Ly 273888a678bbSBryant G. Ly srp->lun.scsi_lun[0] &= 0x3f; 273988a678bbSBryant G. Ly 274088a678bbSBryant G. Ly rc = target_submit_cmd(&cmd->se_cmd, nexus->se_sess, srp->cdb, 274188a678bbSBryant G. Ly cmd->sense_buf, scsilun_to_int(&srp->lun), 274288a678bbSBryant G. Ly data_len, attr, dir, 0); 274388a678bbSBryant G. Ly if (rc) { 274488a678bbSBryant G. Ly dev_err(&vscsi->dev, "target_submit_cmd failed, rc %d\n", rc); 27457435b32eSMichael Cyr spin_lock_bh(&vscsi->intr_lock); 27467435b32eSMichael Cyr list_del(&cmd->list); 27477435b32eSMichael Cyr ibmvscsis_free_cmd_resources(vscsi, cmd); 27487435b32eSMichael Cyr spin_unlock_bh(&vscsi->intr_lock); 274988a678bbSBryant G. Ly goto fail; 275088a678bbSBryant G. Ly } 275188a678bbSBryant G. Ly return; 275288a678bbSBryant G. Ly 275388a678bbSBryant G. Ly fail: 275488a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 275588a678bbSBryant G. Ly ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 275688a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 275788a678bbSBryant G. Ly } 275888a678bbSBryant G. Ly 275988a678bbSBryant G. Ly /** 276088a678bbSBryant G. Ly * ibmvscsis_parse_task() - Parse SRP Task Management Request 276188a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 276288a678bbSBryant G. Ly * @cmd: Pointer to command element with SRP task management request 276388a678bbSBryant G. Ly * 276488a678bbSBryant G. Ly * Parse the srp task management request; if it is valid then submit it to tcm. 276588a678bbSBryant G. Ly * Note: The return code does not reflect the status of the task management 276688a678bbSBryant G. Ly * request. 276788a678bbSBryant G. Ly * 276888a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 276988a678bbSBryant G. Ly * Processor level 277088a678bbSBryant G. Ly */ 277188a678bbSBryant G. Ly static void ibmvscsis_parse_task(struct scsi_info *vscsi, 277288a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd) 277388a678bbSBryant G. Ly { 277488a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 277588a678bbSBryant G. Ly struct srp_tsk_mgmt *srp_tsk = &vio_iu(iue)->srp.tsk_mgmt; 277688a678bbSBryant G. Ly int tcm_type; 277788a678bbSBryant G. Ly u64 tag_to_abort = 0; 277888a678bbSBryant G. Ly int rc = 0; 277988a678bbSBryant G. Ly struct ibmvscsis_nexus *nexus; 278088a678bbSBryant G. Ly 278188a678bbSBryant G. Ly nexus = vscsi->tport.ibmv_nexus; 278288a678bbSBryant G. Ly 278388a678bbSBryant G. Ly cmd->rsp.sol_not = srp_tsk->sol_not; 278488a678bbSBryant G. Ly 278588a678bbSBryant G. Ly switch (srp_tsk->tsk_mgmt_func) { 278688a678bbSBryant G. Ly case SRP_TSK_ABORT_TASK: 278788a678bbSBryant G. Ly tcm_type = TMR_ABORT_TASK; 278888a678bbSBryant G. Ly tag_to_abort = be64_to_cpu(srp_tsk->task_tag); 278988a678bbSBryant G. Ly break; 279088a678bbSBryant G. Ly case SRP_TSK_ABORT_TASK_SET: 279188a678bbSBryant G. Ly tcm_type = TMR_ABORT_TASK_SET; 279288a678bbSBryant G. Ly break; 279388a678bbSBryant G. Ly case SRP_TSK_CLEAR_TASK_SET: 279488a678bbSBryant G. Ly tcm_type = TMR_CLEAR_TASK_SET; 279588a678bbSBryant G. Ly break; 279688a678bbSBryant G. Ly case SRP_TSK_LUN_RESET: 279788a678bbSBryant G. Ly tcm_type = TMR_LUN_RESET; 279888a678bbSBryant G. Ly break; 279988a678bbSBryant G. Ly case SRP_TSK_CLEAR_ACA: 280088a678bbSBryant G. Ly tcm_type = TMR_CLEAR_ACA; 280188a678bbSBryant G. Ly break; 280288a678bbSBryant G. Ly default: 280388a678bbSBryant G. Ly dev_err(&vscsi->dev, "unknown task mgmt func %d\n", 280488a678bbSBryant G. Ly srp_tsk->tsk_mgmt_func); 280588a678bbSBryant G. Ly cmd->se_cmd.se_tmr_req->response = 280688a678bbSBryant G. Ly TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED; 280788a678bbSBryant G. Ly rc = -1; 280888a678bbSBryant G. Ly break; 280988a678bbSBryant G. Ly } 281088a678bbSBryant G. Ly 281188a678bbSBryant G. Ly if (!rc) { 281288a678bbSBryant G. Ly cmd->se_cmd.tag = be64_to_cpu(srp_tsk->tag); 281388a678bbSBryant G. Ly 281488a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 281588a678bbSBryant G. Ly list_add_tail(&cmd->list, &vscsi->active_q); 281688a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 281788a678bbSBryant G. Ly 281888a678bbSBryant G. Ly srp_tsk->lun.scsi_lun[0] &= 0x3f; 281988a678bbSBryant G. Ly 2820417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "calling submit_tmr, func %d\n", 282188a678bbSBryant G. Ly srp_tsk->tsk_mgmt_func); 282288a678bbSBryant G. Ly rc = target_submit_tmr(&cmd->se_cmd, nexus->se_sess, NULL, 282388a678bbSBryant G. Ly scsilun_to_int(&srp_tsk->lun), srp_tsk, 282488a678bbSBryant G. Ly tcm_type, GFP_KERNEL, tag_to_abort, 0); 282588a678bbSBryant G. Ly if (rc) { 282688a678bbSBryant G. Ly dev_err(&vscsi->dev, "target_submit_tmr failed, rc %d\n", 282788a678bbSBryant G. Ly rc); 28287435b32eSMichael Cyr spin_lock_bh(&vscsi->intr_lock); 28297435b32eSMichael Cyr list_del(&cmd->list); 28307435b32eSMichael Cyr spin_unlock_bh(&vscsi->intr_lock); 283188a678bbSBryant G. Ly cmd->se_cmd.se_tmr_req->response = 283288a678bbSBryant G. Ly TMR_FUNCTION_REJECTED; 283388a678bbSBryant G. Ly } 283488a678bbSBryant G. Ly } 283588a678bbSBryant G. Ly 283688a678bbSBryant G. Ly if (rc) 283788a678bbSBryant G. Ly transport_send_check_condition_and_sense(&cmd->se_cmd, 0, 0); 283888a678bbSBryant G. Ly } 283988a678bbSBryant G. Ly 284088a678bbSBryant G. Ly static void ibmvscsis_scheduler(struct work_struct *work) 284188a678bbSBryant G. Ly { 284288a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd = container_of(work, struct ibmvscsis_cmd, 284388a678bbSBryant G. Ly work); 284488a678bbSBryant G. Ly struct scsi_info *vscsi = cmd->adapter; 284588a678bbSBryant G. Ly 284688a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 284788a678bbSBryant G. Ly 284888a678bbSBryant G. Ly /* Remove from schedule_q */ 284988a678bbSBryant G. Ly list_del(&cmd->list); 285088a678bbSBryant G. Ly 285188a678bbSBryant G. Ly /* Don't submit cmd if we're disconnecting */ 285288a678bbSBryant G. Ly if (vscsi->flags & (SCHEDULE_DISCONNECT | DISCONNECT_SCHEDULED)) { 285388a678bbSBryant G. Ly ibmvscsis_free_cmd_resources(vscsi, cmd); 285488a678bbSBryant G. Ly 285588a678bbSBryant G. Ly /* ibmvscsis_disconnect might be waiting for us */ 285688a678bbSBryant G. Ly if (list_empty(&vscsi->active_q) && 285788a678bbSBryant G. Ly list_empty(&vscsi->schedule_q) && 285888a678bbSBryant G. Ly (vscsi->flags & WAIT_FOR_IDLE)) { 285988a678bbSBryant G. Ly vscsi->flags &= ~WAIT_FOR_IDLE; 286088a678bbSBryant G. Ly complete(&vscsi->wait_idle); 286188a678bbSBryant G. Ly } 286288a678bbSBryant G. Ly 286388a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 286488a678bbSBryant G. Ly return; 286588a678bbSBryant G. Ly } 286688a678bbSBryant G. Ly 286788a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 286888a678bbSBryant G. Ly 286988a678bbSBryant G. Ly switch (cmd->type) { 287088a678bbSBryant G. Ly case SCSI_CDB: 287188a678bbSBryant G. Ly ibmvscsis_parse_cmd(vscsi, cmd); 287288a678bbSBryant G. Ly break; 287388a678bbSBryant G. Ly case TASK_MANAGEMENT: 287488a678bbSBryant G. Ly ibmvscsis_parse_task(vscsi, cmd); 287588a678bbSBryant G. Ly break; 287688a678bbSBryant G. Ly default: 287788a678bbSBryant G. Ly dev_err(&vscsi->dev, "scheduler, invalid cmd type %d\n", 287888a678bbSBryant G. Ly cmd->type); 287988a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 288088a678bbSBryant G. Ly ibmvscsis_free_cmd_resources(vscsi, cmd); 288188a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 288288a678bbSBryant G. Ly break; 288388a678bbSBryant G. Ly } 288488a678bbSBryant G. Ly } 288588a678bbSBryant G. Ly 288688a678bbSBryant G. Ly static int ibmvscsis_alloc_cmds(struct scsi_info *vscsi, int num) 288788a678bbSBryant G. Ly { 288888a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd; 288988a678bbSBryant G. Ly int i; 289088a678bbSBryant G. Ly 289188a678bbSBryant G. Ly INIT_LIST_HEAD(&vscsi->free_cmd); 289288a678bbSBryant G. Ly vscsi->cmd_pool = kcalloc(num, sizeof(struct ibmvscsis_cmd), 289388a678bbSBryant G. Ly GFP_KERNEL); 289488a678bbSBryant G. Ly if (!vscsi->cmd_pool) 289588a678bbSBryant G. Ly return -ENOMEM; 289688a678bbSBryant G. Ly 289788a678bbSBryant G. Ly for (i = 0, cmd = (struct ibmvscsis_cmd *)vscsi->cmd_pool; i < num; 289888a678bbSBryant G. Ly i++, cmd++) { 289925e78531SBryant G. Ly cmd->abort_cmd = NULL; 290088a678bbSBryant G. Ly cmd->adapter = vscsi; 290188a678bbSBryant G. Ly INIT_WORK(&cmd->work, ibmvscsis_scheduler); 290288a678bbSBryant G. Ly list_add_tail(&cmd->list, &vscsi->free_cmd); 290388a678bbSBryant G. Ly } 290488a678bbSBryant G. Ly 290588a678bbSBryant G. Ly return 0; 290688a678bbSBryant G. Ly } 290788a678bbSBryant G. Ly 290888a678bbSBryant G. Ly static void ibmvscsis_free_cmds(struct scsi_info *vscsi) 290988a678bbSBryant G. Ly { 291088a678bbSBryant G. Ly kfree(vscsi->cmd_pool); 291188a678bbSBryant G. Ly vscsi->cmd_pool = NULL; 291288a678bbSBryant G. Ly INIT_LIST_HEAD(&vscsi->free_cmd); 291388a678bbSBryant G. Ly } 291488a678bbSBryant G. Ly 291588a678bbSBryant G. Ly /** 291688a678bbSBryant G. Ly * ibmvscsis_service_wait_q() - Service Waiting Queue 291788a678bbSBryant G. Ly * @timer: Pointer to timer which has expired 291888a678bbSBryant G. Ly * 291988a678bbSBryant G. Ly * This routine is called when the timer pops to service the waiting 292088a678bbSBryant G. Ly * queue. Elements on the queue have completed, their responses have been 292188a678bbSBryant G. Ly * copied to the client, but the client's response queue was full so 292288a678bbSBryant G. Ly * the queue message could not be sent. The routine grabs the proper locks 292388a678bbSBryant G. Ly * and calls send messages. 292488a678bbSBryant G. Ly * 292588a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 292688a678bbSBryant G. Ly * called at interrupt level 292788a678bbSBryant G. Ly */ 292888a678bbSBryant G. Ly static enum hrtimer_restart ibmvscsis_service_wait_q(struct hrtimer *timer) 292988a678bbSBryant G. Ly { 293088a678bbSBryant G. Ly struct timer_cb *p_timer = container_of(timer, struct timer_cb, timer); 293188a678bbSBryant G. Ly struct scsi_info *vscsi = container_of(p_timer, struct scsi_info, 293288a678bbSBryant G. Ly rsp_q_timer); 293388a678bbSBryant G. Ly 293488a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 293588a678bbSBryant G. Ly p_timer->timer_pops += 1; 293688a678bbSBryant G. Ly p_timer->started = false; 293788a678bbSBryant G. Ly ibmvscsis_send_messages(vscsi); 293888a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 293988a678bbSBryant G. Ly 294088a678bbSBryant G. Ly return HRTIMER_NORESTART; 294188a678bbSBryant G. Ly } 294288a678bbSBryant G. Ly 294388a678bbSBryant G. Ly static long ibmvscsis_alloctimer(struct scsi_info *vscsi) 294488a678bbSBryant G. Ly { 294588a678bbSBryant G. Ly struct timer_cb *p_timer; 294688a678bbSBryant G. Ly 294788a678bbSBryant G. Ly p_timer = &vscsi->rsp_q_timer; 294888a678bbSBryant G. Ly hrtimer_init(&p_timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 294988a678bbSBryant G. Ly 295088a678bbSBryant G. Ly p_timer->timer.function = ibmvscsis_service_wait_q; 295188a678bbSBryant G. Ly p_timer->started = false; 295288a678bbSBryant G. Ly p_timer->timer_pops = 0; 295388a678bbSBryant G. Ly 295488a678bbSBryant G. Ly return ADAPT_SUCCESS; 295588a678bbSBryant G. Ly } 295688a678bbSBryant G. Ly 295788a678bbSBryant G. Ly static void ibmvscsis_freetimer(struct scsi_info *vscsi) 295888a678bbSBryant G. Ly { 295988a678bbSBryant G. Ly struct timer_cb *p_timer; 296088a678bbSBryant G. Ly 296188a678bbSBryant G. Ly p_timer = &vscsi->rsp_q_timer; 296288a678bbSBryant G. Ly 296388a678bbSBryant G. Ly (void)hrtimer_cancel(&p_timer->timer); 296488a678bbSBryant G. Ly 296588a678bbSBryant G. Ly p_timer->started = false; 296688a678bbSBryant G. Ly p_timer->timer_pops = 0; 296788a678bbSBryant G. Ly } 296888a678bbSBryant G. Ly 296988a678bbSBryant G. Ly static irqreturn_t ibmvscsis_interrupt(int dummy, void *data) 297088a678bbSBryant G. Ly { 297188a678bbSBryant G. Ly struct scsi_info *vscsi = data; 297288a678bbSBryant G. Ly 297388a678bbSBryant G. Ly vio_disable_interrupts(vscsi->dma_dev); 297488a678bbSBryant G. Ly tasklet_schedule(&vscsi->work_task); 297588a678bbSBryant G. Ly 297688a678bbSBryant G. Ly return IRQ_HANDLED; 297788a678bbSBryant G. Ly } 297888a678bbSBryant G. Ly 297988a678bbSBryant G. Ly /** 298088a678bbSBryant G. Ly * ibmvscsis_enable_change_state() - Set new state based on enabled status 298188a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 298288a678bbSBryant G. Ly * 298388a678bbSBryant G. Ly * This function determines our new state now that we are enabled. This 298488a678bbSBryant G. Ly * may involve sending an Init Complete message to the client. 298588a678bbSBryant G. Ly * 298688a678bbSBryant G. Ly * Must be called with interrupt lock held. 298788a678bbSBryant G. Ly */ 298888a678bbSBryant G. Ly static long ibmvscsis_enable_change_state(struct scsi_info *vscsi) 298988a678bbSBryant G. Ly { 2990c9b3379fSMichael Cyr int bytes; 299188a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 299288a678bbSBryant G. Ly 2993c9b3379fSMichael Cyr bytes = vscsi->cmd_q.size * PAGE_SIZE; 2994c9b3379fSMichael Cyr rc = h_reg_crq(vscsi->dds.unit_id, vscsi->cmd_q.crq_token, bytes); 2995c9b3379fSMichael Cyr if (rc == H_CLOSED || rc == H_SUCCESS) { 299688a678bbSBryant G. Ly vscsi->state = WAIT_CONNECTION; 2997c9b3379fSMichael Cyr rc = ibmvscsis_establish_new_q(vscsi); 299888a678bbSBryant G. Ly } 299988a678bbSBryant G. Ly 3000c9b3379fSMichael Cyr if (rc != ADAPT_SUCCESS) { 3001c9b3379fSMichael Cyr vscsi->state = ERR_DISCONNECTED; 3002c9b3379fSMichael Cyr vscsi->flags |= RESPONSE_Q_DOWN; 300388a678bbSBryant G. Ly } 300488a678bbSBryant G. Ly 300588a678bbSBryant G. Ly return rc; 300688a678bbSBryant G. Ly } 300788a678bbSBryant G. Ly 300888a678bbSBryant G. Ly /** 300988a678bbSBryant G. Ly * ibmvscsis_create_command_q() - Create Command Queue 301088a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 301188a678bbSBryant G. Ly * @num_cmds: Currently unused. In the future, may be used to determine 301288a678bbSBryant G. Ly * the size of the CRQ. 301388a678bbSBryant G. Ly * 301488a678bbSBryant G. Ly * Allocates memory for command queue maps remote memory into an ioba 301588a678bbSBryant G. Ly * initializes the command response queue 301688a678bbSBryant G. Ly * 301788a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 301888a678bbSBryant G. Ly * Process level only 301988a678bbSBryant G. Ly */ 302088a678bbSBryant G. Ly static long ibmvscsis_create_command_q(struct scsi_info *vscsi, int num_cmds) 302188a678bbSBryant G. Ly { 302288a678bbSBryant G. Ly int pages; 302388a678bbSBryant G. Ly struct vio_dev *vdev = vscsi->dma_dev; 302488a678bbSBryant G. Ly 302588a678bbSBryant G. Ly /* We might support multiple pages in the future, but just 1 for now */ 302688a678bbSBryant G. Ly pages = 1; 302788a678bbSBryant G. Ly 302888a678bbSBryant G. Ly vscsi->cmd_q.size = pages; 302988a678bbSBryant G. Ly 303088a678bbSBryant G. Ly vscsi->cmd_q.base_addr = 303188a678bbSBryant G. Ly (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL); 303288a678bbSBryant G. Ly if (!vscsi->cmd_q.base_addr) 303388a678bbSBryant G. Ly return -ENOMEM; 303488a678bbSBryant G. Ly 303588a678bbSBryant G. Ly vscsi->cmd_q.mask = ((uint)pages * CRQ_PER_PAGE) - 1; 303688a678bbSBryant G. Ly 303788a678bbSBryant G. Ly vscsi->cmd_q.crq_token = dma_map_single(&vdev->dev, 303888a678bbSBryant G. Ly vscsi->cmd_q.base_addr, 303988a678bbSBryant G. Ly PAGE_SIZE, DMA_BIDIRECTIONAL); 304088a678bbSBryant G. Ly if (dma_mapping_error(&vdev->dev, vscsi->cmd_q.crq_token)) { 304188a678bbSBryant G. Ly free_page((unsigned long)vscsi->cmd_q.base_addr); 304288a678bbSBryant G. Ly return -ENOMEM; 304388a678bbSBryant G. Ly } 304488a678bbSBryant G. Ly 3045c9b3379fSMichael Cyr return 0; 304688a678bbSBryant G. Ly } 304788a678bbSBryant G. Ly 304888a678bbSBryant G. Ly /** 304988a678bbSBryant G. Ly * ibmvscsis_destroy_command_q - Destroy Command Queue 305088a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 305188a678bbSBryant G. Ly * 305288a678bbSBryant G. Ly * Releases memory for command queue and unmaps mapped remote memory. 305388a678bbSBryant G. Ly * 305488a678bbSBryant G. Ly * EXECUTION ENVIRONMENT: 305588a678bbSBryant G. Ly * Process level only 305688a678bbSBryant G. Ly */ 305788a678bbSBryant G. Ly static void ibmvscsis_destroy_command_q(struct scsi_info *vscsi) 305888a678bbSBryant G. Ly { 305988a678bbSBryant G. Ly dma_unmap_single(&vscsi->dma_dev->dev, vscsi->cmd_q.crq_token, 306088a678bbSBryant G. Ly PAGE_SIZE, DMA_BIDIRECTIONAL); 306188a678bbSBryant G. Ly free_page((unsigned long)vscsi->cmd_q.base_addr); 306288a678bbSBryant G. Ly vscsi->cmd_q.base_addr = NULL; 306388a678bbSBryant G. Ly vscsi->state = NO_QUEUE; 306488a678bbSBryant G. Ly } 306588a678bbSBryant G. Ly 306688a678bbSBryant G. Ly static u8 ibmvscsis_fast_fail(struct scsi_info *vscsi, 306788a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd) 306888a678bbSBryant G. Ly { 306988a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 307088a678bbSBryant G. Ly struct se_cmd *se_cmd = &cmd->se_cmd; 307188a678bbSBryant G. Ly struct srp_cmd *srp = (struct srp_cmd *)iue->sbuf->buf; 307288a678bbSBryant G. Ly struct scsi_sense_hdr sshdr; 307388a678bbSBryant G. Ly u8 rc = se_cmd->scsi_status; 307488a678bbSBryant G. Ly 307588a678bbSBryant G. Ly if (vscsi->fast_fail && (READ_CMD(srp->cdb) || WRITE_CMD(srp->cdb))) 307688a678bbSBryant G. Ly if (scsi_normalize_sense(se_cmd->sense_buffer, 307788a678bbSBryant G. Ly se_cmd->scsi_sense_length, &sshdr)) 307888a678bbSBryant G. Ly if (sshdr.sense_key == HARDWARE_ERROR && 307988a678bbSBryant G. Ly (se_cmd->residual_count == 0 || 308088a678bbSBryant G. Ly se_cmd->residual_count == se_cmd->data_length)) { 308188a678bbSBryant G. Ly rc = NO_SENSE; 308288a678bbSBryant G. Ly cmd->flags |= CMD_FAST_FAIL; 308388a678bbSBryant G. Ly } 308488a678bbSBryant G. Ly 308588a678bbSBryant G. Ly return rc; 308688a678bbSBryant G. Ly } 308788a678bbSBryant G. Ly 308888a678bbSBryant G. Ly /** 308988a678bbSBryant G. Ly * srp_build_response() - Build an SRP response buffer 309088a678bbSBryant G. Ly * @vscsi: Pointer to our adapter structure 309188a678bbSBryant G. Ly * @cmd: Pointer to command for which to send the response 309288a678bbSBryant G. Ly * @len_p: Where to return the length of the IU response sent. This 309388a678bbSBryant G. Ly * is needed to construct the CRQ response. 309488a678bbSBryant G. Ly * 309588a678bbSBryant G. Ly * Build the SRP response buffer and copy it to the client's memory space. 309688a678bbSBryant G. Ly */ 309788a678bbSBryant G. Ly static long srp_build_response(struct scsi_info *vscsi, 309888a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd, uint *len_p) 309988a678bbSBryant G. Ly { 310088a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 310188a678bbSBryant G. Ly struct se_cmd *se_cmd = &cmd->se_cmd; 310288a678bbSBryant G. Ly struct srp_rsp *rsp; 310388a678bbSBryant G. Ly uint len; 310488a678bbSBryant G. Ly u32 rsp_code; 310588a678bbSBryant G. Ly char *data; 310688a678bbSBryant G. Ly u32 *tsk_status; 310788a678bbSBryant G. Ly long rc = ADAPT_SUCCESS; 310888a678bbSBryant G. Ly 310988a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 311088a678bbSBryant G. Ly 311188a678bbSBryant G. Ly rsp = &vio_iu(iue)->srp.rsp; 311288a678bbSBryant G. Ly len = sizeof(*rsp); 311388a678bbSBryant G. Ly memset(rsp, 0, len); 311488a678bbSBryant G. Ly data = rsp->data; 311588a678bbSBryant G. Ly 311688a678bbSBryant G. Ly rsp->opcode = SRP_RSP; 311788a678bbSBryant G. Ly 311888a678bbSBryant G. Ly rsp->req_lim_delta = cpu_to_be32(1 + vscsi->credit); 311988a678bbSBryant G. Ly rsp->tag = cmd->rsp.tag; 312088a678bbSBryant G. Ly rsp->flags = 0; 312188a678bbSBryant G. Ly 312288a678bbSBryant G. Ly if (cmd->type == SCSI_CDB) { 312388a678bbSBryant G. Ly rsp->status = ibmvscsis_fast_fail(vscsi, cmd); 312488a678bbSBryant G. Ly if (rsp->status) { 3125417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "build_resp: cmd %p, scsi status %d\n", 3126417dff6cSBryant G. Ly cmd, (int)rsp->status); 312788a678bbSBryant G. Ly ibmvscsis_determine_resid(se_cmd, rsp); 312888a678bbSBryant G. Ly if (se_cmd->scsi_sense_length && se_cmd->sense_buffer) { 312988a678bbSBryant G. Ly rsp->sense_data_len = 313088a678bbSBryant G. Ly cpu_to_be32(se_cmd->scsi_sense_length); 313188a678bbSBryant G. Ly rsp->flags |= SRP_RSP_FLAG_SNSVALID; 313288a678bbSBryant G. Ly len += se_cmd->scsi_sense_length; 313388a678bbSBryant G. Ly memcpy(data, se_cmd->sense_buffer, 313488a678bbSBryant G. Ly se_cmd->scsi_sense_length); 313588a678bbSBryant G. Ly } 313688a678bbSBryant G. Ly rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >> 313788a678bbSBryant G. Ly UCSOLNT_RESP_SHIFT; 313888a678bbSBryant G. Ly } else if (cmd->flags & CMD_FAST_FAIL) { 3139417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "build_resp: cmd %p, fast fail\n", 3140417dff6cSBryant G. Ly cmd); 314188a678bbSBryant G. Ly rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >> 314288a678bbSBryant G. Ly UCSOLNT_RESP_SHIFT; 314388a678bbSBryant G. Ly } else { 314488a678bbSBryant G. Ly rsp->sol_not = (cmd->rsp.sol_not & SCSOLNT) >> 314588a678bbSBryant G. Ly SCSOLNT_RESP_SHIFT; 314688a678bbSBryant G. Ly } 314788a678bbSBryant G. Ly } else { 314888a678bbSBryant G. Ly /* this is task management */ 314988a678bbSBryant G. Ly rsp->status = 0; 315088a678bbSBryant G. Ly rsp->resp_data_len = cpu_to_be32(4); 315188a678bbSBryant G. Ly rsp->flags |= SRP_RSP_FLAG_RSPVALID; 315288a678bbSBryant G. Ly 315388a678bbSBryant G. Ly switch (se_cmd->se_tmr_req->response) { 315488a678bbSBryant G. Ly case TMR_FUNCTION_COMPLETE: 315588a678bbSBryant G. Ly case TMR_TASK_DOES_NOT_EXIST: 315688a678bbSBryant G. Ly rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_COMPLETE; 315788a678bbSBryant G. Ly rsp->sol_not = (cmd->rsp.sol_not & SCSOLNT) >> 315888a678bbSBryant G. Ly SCSOLNT_RESP_SHIFT; 315988a678bbSBryant G. Ly break; 316088a678bbSBryant G. Ly case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED: 316188a678bbSBryant G. Ly case TMR_LUN_DOES_NOT_EXIST: 316288a678bbSBryant G. Ly rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_NOT_SUPPORTED; 316388a678bbSBryant G. Ly rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >> 316488a678bbSBryant G. Ly UCSOLNT_RESP_SHIFT; 316588a678bbSBryant G. Ly break; 316688a678bbSBryant G. Ly case TMR_FUNCTION_FAILED: 316788a678bbSBryant G. Ly case TMR_FUNCTION_REJECTED: 316888a678bbSBryant G. Ly default: 316988a678bbSBryant G. Ly rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_FAILED; 317088a678bbSBryant G. Ly rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >> 317188a678bbSBryant G. Ly UCSOLNT_RESP_SHIFT; 317288a678bbSBryant G. Ly break; 317388a678bbSBryant G. Ly } 317488a678bbSBryant G. Ly 317588a678bbSBryant G. Ly tsk_status = (u32 *)data; 317688a678bbSBryant G. Ly *tsk_status = cpu_to_be32(rsp_code); 317788a678bbSBryant G. Ly data = (char *)(tsk_status + 1); 317888a678bbSBryant G. Ly len += 4; 317988a678bbSBryant G. Ly } 318088a678bbSBryant G. Ly 318188a678bbSBryant G. Ly dma_wmb(); 318288a678bbSBryant G. Ly rc = h_copy_rdma(len, vscsi->dds.window[LOCAL].liobn, iue->sbuf->dma, 318388a678bbSBryant G. Ly vscsi->dds.window[REMOTE].liobn, 318488a678bbSBryant G. Ly be64_to_cpu(iue->remote_token)); 318588a678bbSBryant G. Ly 318688a678bbSBryant G. Ly switch (rc) { 318788a678bbSBryant G. Ly case H_SUCCESS: 318888a678bbSBryant G. Ly vscsi->credit = 0; 318988a678bbSBryant G. Ly *len_p = len; 319088a678bbSBryant G. Ly break; 319188a678bbSBryant G. Ly case H_PERMISSION: 319288a678bbSBryant G. Ly if (connection_broken(vscsi)) 319388a678bbSBryant G. Ly vscsi->flags |= RESPONSE_Q_DOWN | CLIENT_FAILED; 319488a678bbSBryant G. Ly 319588a678bbSBryant G. Ly dev_err(&vscsi->dev, "build_response: error copying to client, rc %ld, flags 0x%x, state 0x%hx\n", 319688a678bbSBryant G. Ly rc, vscsi->flags, vscsi->state); 319788a678bbSBryant G. Ly break; 319888a678bbSBryant G. Ly case H_SOURCE_PARM: 319988a678bbSBryant G. Ly case H_DEST_PARM: 320088a678bbSBryant G. Ly default: 320188a678bbSBryant G. Ly dev_err(&vscsi->dev, "build_response: error copying to client, rc %ld\n", 320288a678bbSBryant G. Ly rc); 320388a678bbSBryant G. Ly break; 320488a678bbSBryant G. Ly } 320588a678bbSBryant G. Ly 320688a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 320788a678bbSBryant G. Ly 320888a678bbSBryant G. Ly return rc; 320988a678bbSBryant G. Ly } 321088a678bbSBryant G. Ly 321188a678bbSBryant G. Ly static int ibmvscsis_rdma(struct ibmvscsis_cmd *cmd, struct scatterlist *sg, 321288a678bbSBryant G. Ly int nsg, struct srp_direct_buf *md, int nmd, 321388a678bbSBryant G. Ly enum dma_data_direction dir, unsigned int bytes) 321488a678bbSBryant G. Ly { 321588a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 321688a678bbSBryant G. Ly struct srp_target *target = iue->target; 321788a678bbSBryant G. Ly struct scsi_info *vscsi = target->ldata; 321888a678bbSBryant G. Ly struct scatterlist *sgp; 321988a678bbSBryant G. Ly dma_addr_t client_ioba, server_ioba; 322088a678bbSBryant G. Ly ulong buf_len; 322188a678bbSBryant G. Ly ulong client_len, server_len; 322288a678bbSBryant G. Ly int md_idx; 322388a678bbSBryant G. Ly long tx_len; 322488a678bbSBryant G. Ly long rc = 0; 322588a678bbSBryant G. Ly 322688a678bbSBryant G. Ly if (bytes == 0) 322788a678bbSBryant G. Ly return 0; 322888a678bbSBryant G. Ly 322988a678bbSBryant G. Ly sgp = sg; 323088a678bbSBryant G. Ly client_len = 0; 323188a678bbSBryant G. Ly server_len = 0; 323288a678bbSBryant G. Ly md_idx = 0; 323388a678bbSBryant G. Ly tx_len = bytes; 323488a678bbSBryant G. Ly 323588a678bbSBryant G. Ly do { 323688a678bbSBryant G. Ly if (client_len == 0) { 323788a678bbSBryant G. Ly if (md_idx >= nmd) { 323888a678bbSBryant G. Ly dev_err(&vscsi->dev, "rdma: ran out of client memory descriptors\n"); 323988a678bbSBryant G. Ly rc = -EIO; 324088a678bbSBryant G. Ly break; 324188a678bbSBryant G. Ly } 324288a678bbSBryant G. Ly client_ioba = be64_to_cpu(md[md_idx].va); 324388a678bbSBryant G. Ly client_len = be32_to_cpu(md[md_idx].len); 324488a678bbSBryant G. Ly } 324588a678bbSBryant G. Ly if (server_len == 0) { 324688a678bbSBryant G. Ly if (!sgp) { 324788a678bbSBryant G. Ly dev_err(&vscsi->dev, "rdma: ran out of scatter/gather list\n"); 324888a678bbSBryant G. Ly rc = -EIO; 324988a678bbSBryant G. Ly break; 325088a678bbSBryant G. Ly } 325188a678bbSBryant G. Ly server_ioba = sg_dma_address(sgp); 325288a678bbSBryant G. Ly server_len = sg_dma_len(sgp); 325388a678bbSBryant G. Ly } 325488a678bbSBryant G. Ly 325588a678bbSBryant G. Ly buf_len = tx_len; 325688a678bbSBryant G. Ly 325788a678bbSBryant G. Ly if (buf_len > client_len) 325888a678bbSBryant G. Ly buf_len = client_len; 325988a678bbSBryant G. Ly 326088a678bbSBryant G. Ly if (buf_len > server_len) 326188a678bbSBryant G. Ly buf_len = server_len; 326288a678bbSBryant G. Ly 326388a678bbSBryant G. Ly if (buf_len > max_vdma_size) 326488a678bbSBryant G. Ly buf_len = max_vdma_size; 326588a678bbSBryant G. Ly 326688a678bbSBryant G. Ly if (dir == DMA_TO_DEVICE) { 326788a678bbSBryant G. Ly /* read from client */ 326888a678bbSBryant G. Ly rc = h_copy_rdma(buf_len, 326988a678bbSBryant G. Ly vscsi->dds.window[REMOTE].liobn, 327088a678bbSBryant G. Ly client_ioba, 327188a678bbSBryant G. Ly vscsi->dds.window[LOCAL].liobn, 327288a678bbSBryant G. Ly server_ioba); 327388a678bbSBryant G. Ly } else { 327488a678bbSBryant G. Ly /* The h_copy_rdma will cause phyp, running in another 327588a678bbSBryant G. Ly * partition, to read memory, so we need to make sure 327688a678bbSBryant G. Ly * the data has been written out, hence these syncs. 327788a678bbSBryant G. Ly */ 327888a678bbSBryant G. Ly /* ensure that everything is in memory */ 327988a678bbSBryant G. Ly isync(); 328088a678bbSBryant G. Ly /* ensure that memory has been made visible */ 328188a678bbSBryant G. Ly dma_wmb(); 328288a678bbSBryant G. Ly rc = h_copy_rdma(buf_len, 328388a678bbSBryant G. Ly vscsi->dds.window[LOCAL].liobn, 328488a678bbSBryant G. Ly server_ioba, 328588a678bbSBryant G. Ly vscsi->dds.window[REMOTE].liobn, 328688a678bbSBryant G. Ly client_ioba); 328788a678bbSBryant G. Ly } 328888a678bbSBryant G. Ly switch (rc) { 328988a678bbSBryant G. Ly case H_SUCCESS: 329088a678bbSBryant G. Ly break; 329188a678bbSBryant G. Ly case H_PERMISSION: 329288a678bbSBryant G. Ly case H_SOURCE_PARM: 329388a678bbSBryant G. Ly case H_DEST_PARM: 329488a678bbSBryant G. Ly if (connection_broken(vscsi)) { 329588a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 329688a678bbSBryant G. Ly vscsi->flags |= 329788a678bbSBryant G. Ly (RESPONSE_Q_DOWN | CLIENT_FAILED); 329888a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 329988a678bbSBryant G. Ly } 330088a678bbSBryant G. Ly dev_err(&vscsi->dev, "rdma: h_copy_rdma failed, rc %ld\n", 330188a678bbSBryant G. Ly rc); 330288a678bbSBryant G. Ly break; 330388a678bbSBryant G. Ly 330488a678bbSBryant G. Ly default: 330588a678bbSBryant G. Ly dev_err(&vscsi->dev, "rdma: unknown error %ld from h_copy_rdma\n", 330688a678bbSBryant G. Ly rc); 330788a678bbSBryant G. Ly break; 330888a678bbSBryant G. Ly } 330988a678bbSBryant G. Ly 331088a678bbSBryant G. Ly if (!rc) { 331188a678bbSBryant G. Ly tx_len -= buf_len; 331288a678bbSBryant G. Ly if (tx_len) { 331388a678bbSBryant G. Ly client_len -= buf_len; 331488a678bbSBryant G. Ly if (client_len == 0) 331588a678bbSBryant G. Ly md_idx++; 331688a678bbSBryant G. Ly else 331788a678bbSBryant G. Ly client_ioba += buf_len; 331888a678bbSBryant G. Ly 331988a678bbSBryant G. Ly server_len -= buf_len; 332088a678bbSBryant G. Ly if (server_len == 0) 332188a678bbSBryant G. Ly sgp = sg_next(sgp); 332288a678bbSBryant G. Ly else 332388a678bbSBryant G. Ly server_ioba += buf_len; 332488a678bbSBryant G. Ly } else { 332588a678bbSBryant G. Ly break; 332688a678bbSBryant G. Ly } 332788a678bbSBryant G. Ly } 332888a678bbSBryant G. Ly } while (!rc); 332988a678bbSBryant G. Ly 333088a678bbSBryant G. Ly return rc; 333188a678bbSBryant G. Ly } 333288a678bbSBryant G. Ly 333388a678bbSBryant G. Ly /** 333488a678bbSBryant G. Ly * ibmvscsis_handle_crq() - Handle CRQ 333588a678bbSBryant G. Ly * @data: Pointer to our adapter structure 333688a678bbSBryant G. Ly * 333788a678bbSBryant G. Ly * Read the command elements from the command queue and copy the payloads 333888a678bbSBryant G. Ly * associated with the command elements to local memory and execute the 333988a678bbSBryant G. Ly * SRP requests. 334088a678bbSBryant G. Ly * 334188a678bbSBryant G. Ly * Note: this is an edge triggered interrupt. It can not be shared. 334288a678bbSBryant G. Ly */ 334388a678bbSBryant G. Ly static void ibmvscsis_handle_crq(unsigned long data) 334488a678bbSBryant G. Ly { 334588a678bbSBryant G. Ly struct scsi_info *vscsi = (struct scsi_info *)data; 334688a678bbSBryant G. Ly struct viosrp_crq *crq; 334788a678bbSBryant G. Ly long rc; 334888a678bbSBryant G. Ly bool ack = true; 334988a678bbSBryant G. Ly volatile u8 valid; 335088a678bbSBryant G. Ly 335188a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 335288a678bbSBryant G. Ly 3353417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "got interrupt\n"); 335488a678bbSBryant G. Ly 335588a678bbSBryant G. Ly /* 335688a678bbSBryant G. Ly * if we are in a path where we are waiting for all pending commands 335788a678bbSBryant G. Ly * to complete because we received a transport event and anything in 335888a678bbSBryant G. Ly * the command queue is for a new connection, do nothing 335988a678bbSBryant G. Ly */ 336088a678bbSBryant G. Ly if (TARGET_STOP(vscsi)) { 336188a678bbSBryant G. Ly vio_enable_interrupts(vscsi->dma_dev); 336288a678bbSBryant G. Ly 3363417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "handle_crq, don't process: flags 0x%x, state 0x%hx\n", 336488a678bbSBryant G. Ly vscsi->flags, vscsi->state); 336588a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 336688a678bbSBryant G. Ly return; 336788a678bbSBryant G. Ly } 336888a678bbSBryant G. Ly 336988a678bbSBryant G. Ly rc = vscsi->flags & SCHEDULE_DISCONNECT; 337088a678bbSBryant G. Ly crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index; 337188a678bbSBryant G. Ly valid = crq->valid; 337288a678bbSBryant G. Ly dma_rmb(); 337388a678bbSBryant G. Ly 337488a678bbSBryant G. Ly while (valid) { 337588a678bbSBryant G. Ly /* 337688a678bbSBryant G. Ly * These are edege triggered interrupts. After dropping out of 337788a678bbSBryant G. Ly * the while loop, the code must check for work since an 337888a678bbSBryant G. Ly * interrupt could be lost, and an elment be left on the queue, 337988a678bbSBryant G. Ly * hence the label. 338088a678bbSBryant G. Ly */ 338188a678bbSBryant G. Ly cmd_work: 338288a678bbSBryant G. Ly vscsi->cmd_q.index = 338388a678bbSBryant G. Ly (vscsi->cmd_q.index + 1) & vscsi->cmd_q.mask; 338488a678bbSBryant G. Ly 338588a678bbSBryant G. Ly if (!rc) { 338688a678bbSBryant G. Ly rc = ibmvscsis_parse_command(vscsi, crq); 338788a678bbSBryant G. Ly } else { 338888a678bbSBryant G. Ly if ((uint)crq->valid == VALID_TRANS_EVENT) { 338988a678bbSBryant G. Ly /* 339088a678bbSBryant G. Ly * must service the transport layer events even 339188a678bbSBryant G. Ly * in an error state, dont break out until all 339288a678bbSBryant G. Ly * the consecutive transport events have been 339388a678bbSBryant G. Ly * processed 339488a678bbSBryant G. Ly */ 339588a678bbSBryant G. Ly rc = ibmvscsis_trans_event(vscsi, crq); 339688a678bbSBryant G. Ly } else if (vscsi->flags & TRANS_EVENT) { 339788a678bbSBryant G. Ly /* 339881290215SBryant G. Ly * if a transport event has occurred leave 339988a678bbSBryant G. Ly * everything but transport events on the queue 340081290215SBryant G. Ly * 340188a678bbSBryant G. Ly * need to decrement the queue index so we can 340279fac9c9SMichael Cyr * look at the element again 340388a678bbSBryant G. Ly */ 340488a678bbSBryant G. Ly if (vscsi->cmd_q.index) 340588a678bbSBryant G. Ly vscsi->cmd_q.index -= 1; 340688a678bbSBryant G. Ly else 340788a678bbSBryant G. Ly /* 340888a678bbSBryant G. Ly * index is at 0 it just wrapped. 340988a678bbSBryant G. Ly * have it index last element in q 341088a678bbSBryant G. Ly */ 341188a678bbSBryant G. Ly vscsi->cmd_q.index = vscsi->cmd_q.mask; 341288a678bbSBryant G. Ly break; 341388a678bbSBryant G. Ly } 341488a678bbSBryant G. Ly } 341588a678bbSBryant G. Ly 341688a678bbSBryant G. Ly crq->valid = INVALIDATE_CMD_RESP_EL; 341788a678bbSBryant G. Ly 341888a678bbSBryant G. Ly crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index; 341988a678bbSBryant G. Ly valid = crq->valid; 342088a678bbSBryant G. Ly dma_rmb(); 342188a678bbSBryant G. Ly } 342288a678bbSBryant G. Ly 342388a678bbSBryant G. Ly if (!rc) { 342488a678bbSBryant G. Ly if (ack) { 342588a678bbSBryant G. Ly vio_enable_interrupts(vscsi->dma_dev); 342688a678bbSBryant G. Ly ack = false; 3427417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "handle_crq, reenabling interrupts\n"); 342888a678bbSBryant G. Ly } 342988a678bbSBryant G. Ly valid = crq->valid; 343088a678bbSBryant G. Ly dma_rmb(); 343188a678bbSBryant G. Ly if (valid) 343288a678bbSBryant G. Ly goto cmd_work; 343388a678bbSBryant G. Ly } else { 3434417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "handle_crq, error: flags 0x%x, state 0x%hx, crq index 0x%x\n", 343588a678bbSBryant G. Ly vscsi->flags, vscsi->state, vscsi->cmd_q.index); 343688a678bbSBryant G. Ly } 343788a678bbSBryant G. Ly 3438417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Leaving handle_crq: schedule_q empty %d, flags 0x%x, state 0x%hx\n", 343988a678bbSBryant G. Ly (int)list_empty(&vscsi->schedule_q), vscsi->flags, 344088a678bbSBryant G. Ly vscsi->state); 344188a678bbSBryant G. Ly 344288a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 344388a678bbSBryant G. Ly } 344488a678bbSBryant G. Ly 344588a678bbSBryant G. Ly static int ibmvscsis_probe(struct vio_dev *vdev, 344688a678bbSBryant G. Ly const struct vio_device_id *id) 344788a678bbSBryant G. Ly { 344888a678bbSBryant G. Ly struct scsi_info *vscsi; 344988a678bbSBryant G. Ly int rc = 0; 345088a678bbSBryant G. Ly long hrc = 0; 345188a678bbSBryant G. Ly char wq_name[24]; 345288a678bbSBryant G. Ly 345388a678bbSBryant G. Ly vscsi = kzalloc(sizeof(*vscsi), GFP_KERNEL); 345488a678bbSBryant G. Ly if (!vscsi) { 345588a678bbSBryant G. Ly rc = -ENOMEM; 3456417dff6cSBryant G. Ly dev_err(&vdev->dev, "probe: allocation of adapter failed\n"); 345788a678bbSBryant G. Ly return rc; 345888a678bbSBryant G. Ly } 345988a678bbSBryant G. Ly 346088a678bbSBryant G. Ly vscsi->dma_dev = vdev; 346188a678bbSBryant G. Ly vscsi->dev = vdev->dev; 346288a678bbSBryant G. Ly INIT_LIST_HEAD(&vscsi->schedule_q); 346388a678bbSBryant G. Ly INIT_LIST_HEAD(&vscsi->waiting_rsp); 346488a678bbSBryant G. Ly INIT_LIST_HEAD(&vscsi->active_q); 346588a678bbSBryant G. Ly 346611950d70SMichael Cyr snprintf(vscsi->tport.tport_name, IBMVSCSIS_NAMELEN, "%s", 346711950d70SMichael Cyr dev_name(&vdev->dev)); 346888a678bbSBryant G. Ly 3469417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "probe tport_name: %s\n", vscsi->tport.tport_name); 347088a678bbSBryant G. Ly 347188a678bbSBryant G. Ly rc = read_dma_window(vscsi); 347288a678bbSBryant G. Ly if (rc) 347388a678bbSBryant G. Ly goto free_adapter; 3474417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "Probe: liobn 0x%x, riobn 0x%x\n", 347588a678bbSBryant G. Ly vscsi->dds.window[LOCAL].liobn, 347688a678bbSBryant G. Ly vscsi->dds.window[REMOTE].liobn); 347788a678bbSBryant G. Ly 347888a678bbSBryant G. Ly strcpy(vscsi->eye, "VSCSI "); 347988a678bbSBryant G. Ly strncat(vscsi->eye, vdev->name, MAX_EYE); 348088a678bbSBryant G. Ly 348188a678bbSBryant G. Ly vscsi->dds.unit_id = vdev->unit_address; 34829c93cf03SMichael Cyr strncpy(vscsi->dds.partition_name, partition_name, 34839c93cf03SMichael Cyr sizeof(vscsi->dds.partition_name)); 34849c93cf03SMichael Cyr vscsi->dds.partition_num = partition_number; 348588a678bbSBryant G. Ly 348688a678bbSBryant G. Ly spin_lock_bh(&ibmvscsis_dev_lock); 348788a678bbSBryant G. Ly list_add_tail(&vscsi->list, &ibmvscsis_dev_list); 348888a678bbSBryant G. Ly spin_unlock_bh(&ibmvscsis_dev_lock); 348988a678bbSBryant G. Ly 349088a678bbSBryant G. Ly /* 349188a678bbSBryant G. Ly * TBD: How do we determine # of cmds to request? Do we know how 349288a678bbSBryant G. Ly * many "children" we have? 349388a678bbSBryant G. Ly */ 349488a678bbSBryant G. Ly vscsi->request_limit = INITIAL_SRP_LIMIT; 349588a678bbSBryant G. Ly rc = srp_target_alloc(&vscsi->target, &vdev->dev, vscsi->request_limit, 349688a678bbSBryant G. Ly SRP_MAX_IU_LEN); 349788a678bbSBryant G. Ly if (rc) 349888a678bbSBryant G. Ly goto rem_list; 349988a678bbSBryant G. Ly 350088a678bbSBryant G. Ly vscsi->target.ldata = vscsi; 350188a678bbSBryant G. Ly 350288a678bbSBryant G. Ly rc = ibmvscsis_alloc_cmds(vscsi, vscsi->request_limit); 350388a678bbSBryant G. Ly if (rc) { 350488a678bbSBryant G. Ly dev_err(&vscsi->dev, "alloc_cmds failed, rc %d, num %d\n", 350588a678bbSBryant G. Ly rc, vscsi->request_limit); 350688a678bbSBryant G. Ly goto free_target; 350788a678bbSBryant G. Ly } 350888a678bbSBryant G. Ly 350988a678bbSBryant G. Ly /* 351088a678bbSBryant G. Ly * Note: the lock is used in freeing timers, so must initialize 351188a678bbSBryant G. Ly * first so that ordering in case of error is correct. 351288a678bbSBryant G. Ly */ 351388a678bbSBryant G. Ly spin_lock_init(&vscsi->intr_lock); 351488a678bbSBryant G. Ly 351588a678bbSBryant G. Ly rc = ibmvscsis_alloctimer(vscsi); 351688a678bbSBryant G. Ly if (rc) { 351788a678bbSBryant G. Ly dev_err(&vscsi->dev, "probe: alloctimer failed, rc %d\n", rc); 351888a678bbSBryant G. Ly goto free_cmds; 351988a678bbSBryant G. Ly } 352088a678bbSBryant G. Ly 352188a678bbSBryant G. Ly rc = ibmvscsis_create_command_q(vscsi, 256); 352288a678bbSBryant G. Ly if (rc) { 352388a678bbSBryant G. Ly dev_err(&vscsi->dev, "probe: create_command_q failed, rc %d\n", 352488a678bbSBryant G. Ly rc); 352588a678bbSBryant G. Ly goto free_timer; 352688a678bbSBryant G. Ly } 352788a678bbSBryant G. Ly 352888a678bbSBryant G. Ly vscsi->map_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); 352988a678bbSBryant G. Ly if (!vscsi->map_buf) { 353088a678bbSBryant G. Ly rc = -ENOMEM; 353188a678bbSBryant G. Ly dev_err(&vscsi->dev, "probe: allocating cmd buffer failed\n"); 353288a678bbSBryant G. Ly goto destroy_queue; 353388a678bbSBryant G. Ly } 353488a678bbSBryant G. Ly 353588a678bbSBryant G. Ly vscsi->map_ioba = dma_map_single(&vdev->dev, vscsi->map_buf, PAGE_SIZE, 353688a678bbSBryant G. Ly DMA_BIDIRECTIONAL); 353788a678bbSBryant G. Ly if (dma_mapping_error(&vdev->dev, vscsi->map_ioba)) { 353838247febSWei Yongjun rc = -ENOMEM; 353988a678bbSBryant G. Ly dev_err(&vscsi->dev, "probe: error mapping command buffer\n"); 354088a678bbSBryant G. Ly goto free_buf; 354188a678bbSBryant G. Ly } 354288a678bbSBryant G. Ly 354388a678bbSBryant G. Ly hrc = h_vioctl(vscsi->dds.unit_id, H_GET_PARTNER_INFO, 354488a678bbSBryant G. Ly (u64)vscsi->map_ioba | ((u64)PAGE_SIZE << 32), 0, 0, 0, 354588a678bbSBryant G. Ly 0); 354688a678bbSBryant G. Ly if (hrc == H_SUCCESS) 354788a678bbSBryant G. Ly vscsi->client_data.partition_number = 354888a678bbSBryant G. Ly be64_to_cpu(*(u64 *)vscsi->map_buf); 354988a678bbSBryant G. Ly /* 355088a678bbSBryant G. Ly * We expect the VIOCTL to fail if we're configured as "any 355188a678bbSBryant G. Ly * client can connect" and the client isn't activated yet. 355288a678bbSBryant G. Ly * We'll make the call again when he sends an init msg. 355388a678bbSBryant G. Ly */ 3554417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "probe hrc %ld, client partition num %d\n", 355588a678bbSBryant G. Ly hrc, vscsi->client_data.partition_number); 355688a678bbSBryant G. Ly 355788a678bbSBryant G. Ly tasklet_init(&vscsi->work_task, ibmvscsis_handle_crq, 355888a678bbSBryant G. Ly (unsigned long)vscsi); 355988a678bbSBryant G. Ly 356088a678bbSBryant G. Ly init_completion(&vscsi->wait_idle); 35618bf11557SMichael Cyr init_completion(&vscsi->unconfig); 356288a678bbSBryant G. Ly 356388a678bbSBryant G. Ly snprintf(wq_name, 24, "ibmvscsis%s", dev_name(&vdev->dev)); 356488a678bbSBryant G. Ly vscsi->work_q = create_workqueue(wq_name); 356588a678bbSBryant G. Ly if (!vscsi->work_q) { 356688a678bbSBryant G. Ly rc = -ENOMEM; 356788a678bbSBryant G. Ly dev_err(&vscsi->dev, "create_workqueue failed\n"); 356888a678bbSBryant G. Ly goto unmap_buf; 356988a678bbSBryant G. Ly } 357088a678bbSBryant G. Ly 357188a678bbSBryant G. Ly rc = request_irq(vdev->irq, ibmvscsis_interrupt, 0, "ibmvscsis", vscsi); 357288a678bbSBryant G. Ly if (rc) { 357388a678bbSBryant G. Ly rc = -EPERM; 357488a678bbSBryant G. Ly dev_err(&vscsi->dev, "probe: request_irq failed, rc %d\n", rc); 357588a678bbSBryant G. Ly goto destroy_WQ; 357688a678bbSBryant G. Ly } 357788a678bbSBryant G. Ly 3578c9b3379fSMichael Cyr vscsi->state = WAIT_ENABLED; 357988a678bbSBryant G. Ly 358088a678bbSBryant G. Ly dev_set_drvdata(&vdev->dev, vscsi); 358188a678bbSBryant G. Ly 358288a678bbSBryant G. Ly return 0; 358388a678bbSBryant G. Ly 358488a678bbSBryant G. Ly destroy_WQ: 358588a678bbSBryant G. Ly destroy_workqueue(vscsi->work_q); 358688a678bbSBryant G. Ly unmap_buf: 358788a678bbSBryant G. Ly dma_unmap_single(&vdev->dev, vscsi->map_ioba, PAGE_SIZE, 358888a678bbSBryant G. Ly DMA_BIDIRECTIONAL); 358988a678bbSBryant G. Ly free_buf: 359088a678bbSBryant G. Ly kfree(vscsi->map_buf); 359188a678bbSBryant G. Ly destroy_queue: 359288a678bbSBryant G. Ly tasklet_kill(&vscsi->work_task); 359388a678bbSBryant G. Ly ibmvscsis_unregister_command_q(vscsi); 359488a678bbSBryant G. Ly ibmvscsis_destroy_command_q(vscsi); 359588a678bbSBryant G. Ly free_timer: 359688a678bbSBryant G. Ly ibmvscsis_freetimer(vscsi); 359788a678bbSBryant G. Ly free_cmds: 359888a678bbSBryant G. Ly ibmvscsis_free_cmds(vscsi); 359988a678bbSBryant G. Ly free_target: 360088a678bbSBryant G. Ly srp_target_free(&vscsi->target); 360188a678bbSBryant G. Ly rem_list: 360288a678bbSBryant G. Ly spin_lock_bh(&ibmvscsis_dev_lock); 360388a678bbSBryant G. Ly list_del(&vscsi->list); 360488a678bbSBryant G. Ly spin_unlock_bh(&ibmvscsis_dev_lock); 360588a678bbSBryant G. Ly free_adapter: 360688a678bbSBryant G. Ly kfree(vscsi); 360788a678bbSBryant G. Ly 360888a678bbSBryant G. Ly return rc; 360988a678bbSBryant G. Ly } 361088a678bbSBryant G. Ly 361188a678bbSBryant G. Ly static int ibmvscsis_remove(struct vio_dev *vdev) 361288a678bbSBryant G. Ly { 361388a678bbSBryant G. Ly struct scsi_info *vscsi = dev_get_drvdata(&vdev->dev); 361488a678bbSBryant G. Ly 3615417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "remove (%s)\n", dev_name(&vscsi->dma_dev->dev)); 361688a678bbSBryant G. Ly 36178bf11557SMichael Cyr spin_lock_bh(&vscsi->intr_lock); 36188bf11557SMichael Cyr ibmvscsis_post_disconnect(vscsi, UNCONFIGURING, 0); 36198bf11557SMichael Cyr vscsi->flags |= CFG_SLEEPING; 36208bf11557SMichael Cyr spin_unlock_bh(&vscsi->intr_lock); 36218bf11557SMichael Cyr wait_for_completion(&vscsi->unconfig); 362288a678bbSBryant G. Ly 362388a678bbSBryant G. Ly vio_disable_interrupts(vdev); 362488a678bbSBryant G. Ly free_irq(vdev->irq, vscsi); 362588a678bbSBryant G. Ly destroy_workqueue(vscsi->work_q); 362688a678bbSBryant G. Ly dma_unmap_single(&vdev->dev, vscsi->map_ioba, PAGE_SIZE, 362788a678bbSBryant G. Ly DMA_BIDIRECTIONAL); 362888a678bbSBryant G. Ly kfree(vscsi->map_buf); 362988a678bbSBryant G. Ly tasklet_kill(&vscsi->work_task); 363088a678bbSBryant G. Ly ibmvscsis_destroy_command_q(vscsi); 363188a678bbSBryant G. Ly ibmvscsis_freetimer(vscsi); 363288a678bbSBryant G. Ly ibmvscsis_free_cmds(vscsi); 363388a678bbSBryant G. Ly srp_target_free(&vscsi->target); 363488a678bbSBryant G. Ly spin_lock_bh(&ibmvscsis_dev_lock); 363588a678bbSBryant G. Ly list_del(&vscsi->list); 363688a678bbSBryant G. Ly spin_unlock_bh(&ibmvscsis_dev_lock); 363788a678bbSBryant G. Ly kfree(vscsi); 363888a678bbSBryant G. Ly 363988a678bbSBryant G. Ly return 0; 364088a678bbSBryant G. Ly } 364188a678bbSBryant G. Ly 364288a678bbSBryant G. Ly static ssize_t system_id_show(struct device *dev, 364388a678bbSBryant G. Ly struct device_attribute *attr, char *buf) 364488a678bbSBryant G. Ly { 364588a678bbSBryant G. Ly return snprintf(buf, PAGE_SIZE, "%s\n", system_id); 364688a678bbSBryant G. Ly } 364788a678bbSBryant G. Ly 364888a678bbSBryant G. Ly static ssize_t partition_number_show(struct device *dev, 364988a678bbSBryant G. Ly struct device_attribute *attr, char *buf) 365088a678bbSBryant G. Ly { 365188a678bbSBryant G. Ly return snprintf(buf, PAGE_SIZE, "%x\n", partition_number); 365288a678bbSBryant G. Ly } 365388a678bbSBryant G. Ly 365488a678bbSBryant G. Ly static ssize_t unit_address_show(struct device *dev, 365588a678bbSBryant G. Ly struct device_attribute *attr, char *buf) 365688a678bbSBryant G. Ly { 365788a678bbSBryant G. Ly struct scsi_info *vscsi = container_of(dev, struct scsi_info, dev); 365888a678bbSBryant G. Ly 365988a678bbSBryant G. Ly return snprintf(buf, PAGE_SIZE, "%x\n", vscsi->dma_dev->unit_address); 366088a678bbSBryant G. Ly } 366188a678bbSBryant G. Ly 366288a678bbSBryant G. Ly static int ibmvscsis_get_system_info(void) 366388a678bbSBryant G. Ly { 366488a678bbSBryant G. Ly struct device_node *rootdn, *vdevdn; 366588a678bbSBryant G. Ly const char *id, *model, *name; 366688a678bbSBryant G. Ly const uint *num; 366788a678bbSBryant G. Ly 366888a678bbSBryant G. Ly rootdn = of_find_node_by_path("/"); 366988a678bbSBryant G. Ly if (!rootdn) 367088a678bbSBryant G. Ly return -ENOENT; 367188a678bbSBryant G. Ly 367288a678bbSBryant G. Ly model = of_get_property(rootdn, "model", NULL); 367388a678bbSBryant G. Ly id = of_get_property(rootdn, "system-id", NULL); 367488a678bbSBryant G. Ly if (model && id) 367588a678bbSBryant G. Ly snprintf(system_id, sizeof(system_id), "%s-%s", model, id); 367688a678bbSBryant G. Ly 367788a678bbSBryant G. Ly name = of_get_property(rootdn, "ibm,partition-name", NULL); 367888a678bbSBryant G. Ly if (name) 367988a678bbSBryant G. Ly strncpy(partition_name, name, sizeof(partition_name)); 368088a678bbSBryant G. Ly 368188a678bbSBryant G. Ly num = of_get_property(rootdn, "ibm,partition-no", NULL); 368288a678bbSBryant G. Ly if (num) 36839c93cf03SMichael Cyr partition_number = of_read_number(num, 1); 368488a678bbSBryant G. Ly 368588a678bbSBryant G. Ly of_node_put(rootdn); 368688a678bbSBryant G. Ly 368788a678bbSBryant G. Ly vdevdn = of_find_node_by_path("/vdevice"); 368888a678bbSBryant G. Ly if (vdevdn) { 368988a678bbSBryant G. Ly const uint *mvds; 369088a678bbSBryant G. Ly 369188a678bbSBryant G. Ly mvds = of_get_property(vdevdn, "ibm,max-virtual-dma-size", 369288a678bbSBryant G. Ly NULL); 369388a678bbSBryant G. Ly if (mvds) 369488a678bbSBryant G. Ly max_vdma_size = *mvds; 369588a678bbSBryant G. Ly of_node_put(vdevdn); 369688a678bbSBryant G. Ly } 369788a678bbSBryant G. Ly 369888a678bbSBryant G. Ly return 0; 369988a678bbSBryant G. Ly } 370088a678bbSBryant G. Ly 370188a678bbSBryant G. Ly static char *ibmvscsis_get_fabric_name(void) 370288a678bbSBryant G. Ly { 370388a678bbSBryant G. Ly return "ibmvscsis"; 370488a678bbSBryant G. Ly } 370588a678bbSBryant G. Ly 370688a678bbSBryant G. Ly static char *ibmvscsis_get_fabric_wwn(struct se_portal_group *se_tpg) 370788a678bbSBryant G. Ly { 370888a678bbSBryant G. Ly struct ibmvscsis_tport *tport = 370988a678bbSBryant G. Ly container_of(se_tpg, struct ibmvscsis_tport, se_tpg); 371088a678bbSBryant G. Ly 371188a678bbSBryant G. Ly return tport->tport_name; 371288a678bbSBryant G. Ly } 371388a678bbSBryant G. Ly 371488a678bbSBryant G. Ly static u16 ibmvscsis_get_tag(struct se_portal_group *se_tpg) 371588a678bbSBryant G. Ly { 371688a678bbSBryant G. Ly struct ibmvscsis_tport *tport = 371788a678bbSBryant G. Ly container_of(se_tpg, struct ibmvscsis_tport, se_tpg); 371888a678bbSBryant G. Ly 371988a678bbSBryant G. Ly return tport->tport_tpgt; 372088a678bbSBryant G. Ly } 372188a678bbSBryant G. Ly 372288a678bbSBryant G. Ly static u32 ibmvscsis_get_default_depth(struct se_portal_group *se_tpg) 372388a678bbSBryant G. Ly { 372488a678bbSBryant G. Ly return 1; 372588a678bbSBryant G. Ly } 372688a678bbSBryant G. Ly 372788a678bbSBryant G. Ly static int ibmvscsis_check_true(struct se_portal_group *se_tpg) 372888a678bbSBryant G. Ly { 372988a678bbSBryant G. Ly return 1; 373088a678bbSBryant G. Ly } 373188a678bbSBryant G. Ly 373288a678bbSBryant G. Ly static int ibmvscsis_check_false(struct se_portal_group *se_tpg) 373388a678bbSBryant G. Ly { 373488a678bbSBryant G. Ly return 0; 373588a678bbSBryant G. Ly } 373688a678bbSBryant G. Ly 373788a678bbSBryant G. Ly static u32 ibmvscsis_tpg_get_inst_index(struct se_portal_group *se_tpg) 373888a678bbSBryant G. Ly { 373988a678bbSBryant G. Ly return 1; 374088a678bbSBryant G. Ly } 374188a678bbSBryant G. Ly 374288a678bbSBryant G. Ly static int ibmvscsis_check_stop_free(struct se_cmd *se_cmd) 374388a678bbSBryant G. Ly { 374488a678bbSBryant G. Ly return target_put_sess_cmd(se_cmd); 374588a678bbSBryant G. Ly } 374688a678bbSBryant G. Ly 374788a678bbSBryant G. Ly static void ibmvscsis_release_cmd(struct se_cmd *se_cmd) 374888a678bbSBryant G. Ly { 374988a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd, 375088a678bbSBryant G. Ly se_cmd); 375188a678bbSBryant G. Ly struct scsi_info *vscsi = cmd->adapter; 375288a678bbSBryant G. Ly 375388a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 375488a678bbSBryant G. Ly /* Remove from active_q */ 3755980b3271SWei Yongjun list_move_tail(&cmd->list, &vscsi->waiting_rsp); 375688a678bbSBryant G. Ly ibmvscsis_send_messages(vscsi); 375788a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 375888a678bbSBryant G. Ly } 375988a678bbSBryant G. Ly 376088a678bbSBryant G. Ly static u32 ibmvscsis_sess_get_index(struct se_session *se_sess) 376188a678bbSBryant G. Ly { 376288a678bbSBryant G. Ly return 0; 376388a678bbSBryant G. Ly } 376488a678bbSBryant G. Ly 376588a678bbSBryant G. Ly static int ibmvscsis_write_pending(struct se_cmd *se_cmd) 376688a678bbSBryant G. Ly { 376788a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd, 376888a678bbSBryant G. Ly se_cmd); 376925e78531SBryant G. Ly struct scsi_info *vscsi = cmd->adapter; 377088a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 377188a678bbSBryant G. Ly int rc; 377288a678bbSBryant G. Ly 377325e78531SBryant G. Ly /* 377425e78531SBryant G. Ly * If CLIENT_FAILED OR RESPONSE_Q_DOWN, then just return success 377525e78531SBryant G. Ly * since LIO can't do anything about it, and we dont want to 377625e78531SBryant G. Ly * attempt an srp_transfer_data. 377725e78531SBryant G. Ly */ 377825e78531SBryant G. Ly if ((vscsi->flags & (CLIENT_FAILED | RESPONSE_Q_DOWN))) { 3779417dff6cSBryant G. Ly dev_err(&vscsi->dev, "write_pending failed since: %d\n", 3780417dff6cSBryant G. Ly vscsi->flags); 378188e65389SBryant G. Ly return -EIO; 3782417dff6cSBryant G. Ly 378325e78531SBryant G. Ly } 378425e78531SBryant G. Ly 378588a678bbSBryant G. Ly rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma, 378688a678bbSBryant G. Ly 1, 1); 378788a678bbSBryant G. Ly if (rc) { 3788417dff6cSBryant G. Ly dev_err(&vscsi->dev, "srp_transfer_data() failed: %d\n", rc); 37897c9d8d0cSBryant G. Ly return -EIO; 379088a678bbSBryant G. Ly } 379188a678bbSBryant G. Ly /* 379288a678bbSBryant G. Ly * We now tell TCM to add this WRITE CDB directly into the TCM storage 379388a678bbSBryant G. Ly * object execution queue. 379488a678bbSBryant G. Ly */ 379588a678bbSBryant G. Ly target_execute_cmd(se_cmd); 379688a678bbSBryant G. Ly return 0; 379788a678bbSBryant G. Ly } 379888a678bbSBryant G. Ly 379988a678bbSBryant G. Ly static int ibmvscsis_write_pending_status(struct se_cmd *se_cmd) 380088a678bbSBryant G. Ly { 380188a678bbSBryant G. Ly return 0; 380288a678bbSBryant G. Ly } 380388a678bbSBryant G. Ly 380488a678bbSBryant G. Ly static void ibmvscsis_set_default_node_attrs(struct se_node_acl *nacl) 380588a678bbSBryant G. Ly { 380688a678bbSBryant G. Ly } 380788a678bbSBryant G. Ly 380888a678bbSBryant G. Ly static int ibmvscsis_get_cmd_state(struct se_cmd *se_cmd) 380988a678bbSBryant G. Ly { 381088a678bbSBryant G. Ly return 0; 381188a678bbSBryant G. Ly } 381288a678bbSBryant G. Ly 381388a678bbSBryant G. Ly static int ibmvscsis_queue_data_in(struct se_cmd *se_cmd) 381488a678bbSBryant G. Ly { 381588a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd, 381688a678bbSBryant G. Ly se_cmd); 381788a678bbSBryant G. Ly struct iu_entry *iue = cmd->iue; 381888a678bbSBryant G. Ly struct scsi_info *vscsi = cmd->adapter; 381988a678bbSBryant G. Ly char *sd; 382088a678bbSBryant G. Ly uint len = 0; 382188a678bbSBryant G. Ly int rc; 382288a678bbSBryant G. Ly 382388a678bbSBryant G. Ly rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma, 1, 382488a678bbSBryant G. Ly 1); 382588a678bbSBryant G. Ly if (rc) { 3826417dff6cSBryant G. Ly dev_err(&vscsi->dev, "srp_transfer_data failed: %d\n", rc); 382788a678bbSBryant G. Ly sd = se_cmd->sense_buffer; 382888a678bbSBryant G. Ly se_cmd->scsi_sense_length = 18; 382988a678bbSBryant G. Ly memset(se_cmd->sense_buffer, 0, se_cmd->scsi_sense_length); 383088a678bbSBryant G. Ly /* Logical Unit Communication Time-out asc/ascq = 0x0801 */ 383188a678bbSBryant G. Ly scsi_build_sense_buffer(0, se_cmd->sense_buffer, MEDIUM_ERROR, 383288a678bbSBryant G. Ly 0x08, 0x01); 383388a678bbSBryant G. Ly } 383488a678bbSBryant G. Ly 383588a678bbSBryant G. Ly srp_build_response(vscsi, cmd, &len); 383688a678bbSBryant G. Ly cmd->rsp.format = SRP_FORMAT; 383788a678bbSBryant G. Ly cmd->rsp.len = len; 383888a678bbSBryant G. Ly 383988a678bbSBryant G. Ly return 0; 384088a678bbSBryant G. Ly } 384188a678bbSBryant G. Ly 384288a678bbSBryant G. Ly static int ibmvscsis_queue_status(struct se_cmd *se_cmd) 384388a678bbSBryant G. Ly { 384488a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd, 384588a678bbSBryant G. Ly se_cmd); 384688a678bbSBryant G. Ly struct scsi_info *vscsi = cmd->adapter; 384788a678bbSBryant G. Ly uint len; 384888a678bbSBryant G. Ly 3849417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "queue_status %p\n", se_cmd); 385088a678bbSBryant G. Ly 385188a678bbSBryant G. Ly srp_build_response(vscsi, cmd, &len); 385288a678bbSBryant G. Ly cmd->rsp.format = SRP_FORMAT; 385388a678bbSBryant G. Ly cmd->rsp.len = len; 385488a678bbSBryant G. Ly 385588a678bbSBryant G. Ly return 0; 385688a678bbSBryant G. Ly } 385788a678bbSBryant G. Ly 385888a678bbSBryant G. Ly static void ibmvscsis_queue_tm_rsp(struct se_cmd *se_cmd) 385988a678bbSBryant G. Ly { 386088a678bbSBryant G. Ly struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd, 386188a678bbSBryant G. Ly se_cmd); 386288a678bbSBryant G. Ly struct scsi_info *vscsi = cmd->adapter; 386325e78531SBryant G. Ly struct ibmvscsis_cmd *cmd_itr; 386425e78531SBryant G. Ly struct iu_entry *iue = iue = cmd->iue; 386525e78531SBryant G. Ly struct srp_tsk_mgmt *srp_tsk = &vio_iu(iue)->srp.tsk_mgmt; 386625e78531SBryant G. Ly u64 tag_to_abort = be64_to_cpu(srp_tsk->task_tag); 386788a678bbSBryant G. Ly uint len; 386888a678bbSBryant G. Ly 3869417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "queue_tm_rsp %p, status %d\n", 387088a678bbSBryant G. Ly se_cmd, (int)se_cmd->se_tmr_req->response); 387188a678bbSBryant G. Ly 387225e78531SBryant G. Ly if (srp_tsk->tsk_mgmt_func == SRP_TSK_ABORT_TASK && 387325e78531SBryant G. Ly cmd->se_cmd.se_tmr_req->response == TMR_TASK_DOES_NOT_EXIST) { 387425e78531SBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 387525e78531SBryant G. Ly list_for_each_entry(cmd_itr, &vscsi->active_q, list) { 387625e78531SBryant G. Ly if (tag_to_abort == cmd_itr->se_cmd.tag) { 387725e78531SBryant G. Ly cmd_itr->abort_cmd = cmd; 387825e78531SBryant G. Ly cmd->flags |= DELAY_SEND; 387925e78531SBryant G. Ly break; 388025e78531SBryant G. Ly } 388125e78531SBryant G. Ly } 388225e78531SBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 388325e78531SBryant G. Ly } 388425e78531SBryant G. Ly 388588a678bbSBryant G. Ly srp_build_response(vscsi, cmd, &len); 388688a678bbSBryant G. Ly cmd->rsp.format = SRP_FORMAT; 388788a678bbSBryant G. Ly cmd->rsp.len = len; 388888a678bbSBryant G. Ly } 388988a678bbSBryant G. Ly 389088a678bbSBryant G. Ly static void ibmvscsis_aborted_task(struct se_cmd *se_cmd) 389188a678bbSBryant G. Ly { 3892417dff6cSBryant G. Ly struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd, 3893417dff6cSBryant G. Ly se_cmd); 3894417dff6cSBryant G. Ly struct scsi_info *vscsi = cmd->adapter; 3895417dff6cSBryant G. Ly 3896417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "ibmvscsis_aborted_task %p task_tag: %llu\n", 389725e78531SBryant G. Ly se_cmd, se_cmd->tag); 389888a678bbSBryant G. Ly } 389988a678bbSBryant G. Ly 390088a678bbSBryant G. Ly static struct se_wwn *ibmvscsis_make_tport(struct target_fabric_configfs *tf, 390188a678bbSBryant G. Ly struct config_group *group, 390288a678bbSBryant G. Ly const char *name) 390388a678bbSBryant G. Ly { 390488a678bbSBryant G. Ly struct ibmvscsis_tport *tport; 3905417dff6cSBryant G. Ly struct scsi_info *vscsi; 390688a678bbSBryant G. Ly 390788a678bbSBryant G. Ly tport = ibmvscsis_lookup_port(name); 390888a678bbSBryant G. Ly if (tport) { 3909417dff6cSBryant G. Ly vscsi = container_of(tport, struct scsi_info, tport); 391088a678bbSBryant G. Ly tport->tport_proto_id = SCSI_PROTOCOL_SRP; 3911417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "make_tport(%s), pointer:%p, tport_id:%x\n", 391288a678bbSBryant G. Ly name, tport, tport->tport_proto_id); 391388a678bbSBryant G. Ly return &tport->tport_wwn; 391488a678bbSBryant G. Ly } 391588a678bbSBryant G. Ly 391688a678bbSBryant G. Ly return ERR_PTR(-EINVAL); 391788a678bbSBryant G. Ly } 391888a678bbSBryant G. Ly 391988a678bbSBryant G. Ly static void ibmvscsis_drop_tport(struct se_wwn *wwn) 392088a678bbSBryant G. Ly { 392188a678bbSBryant G. Ly struct ibmvscsis_tport *tport = container_of(wwn, 392288a678bbSBryant G. Ly struct ibmvscsis_tport, 392388a678bbSBryant G. Ly tport_wwn); 3924417dff6cSBryant G. Ly struct scsi_info *vscsi = container_of(tport, struct scsi_info, tport); 392588a678bbSBryant G. Ly 3926417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "drop_tport(%s)\n", 392788a678bbSBryant G. Ly config_item_name(&tport->tport_wwn.wwn_group.cg_item)); 392888a678bbSBryant G. Ly } 392988a678bbSBryant G. Ly 393088a678bbSBryant G. Ly static struct se_portal_group *ibmvscsis_make_tpg(struct se_wwn *wwn, 393188a678bbSBryant G. Ly const char *name) 393288a678bbSBryant G. Ly { 393388a678bbSBryant G. Ly struct ibmvscsis_tport *tport = 393488a678bbSBryant G. Ly container_of(wwn, struct ibmvscsis_tport, tport_wwn); 3935e9447a46SBryant G. Ly u16 tpgt; 393688a678bbSBryant G. Ly int rc; 393788a678bbSBryant G. Ly 3938e9447a46SBryant G. Ly if (strstr(name, "tpgt_") != name) 3939e9447a46SBryant G. Ly return ERR_PTR(-EINVAL); 3940e9447a46SBryant G. Ly rc = kstrtou16(name + 5, 0, &tpgt); 3941e9447a46SBryant G. Ly if (rc) 3942e9447a46SBryant G. Ly return ERR_PTR(rc); 3943e9447a46SBryant G. Ly tport->tport_tpgt = tpgt; 3944e9447a46SBryant G. Ly 394588a678bbSBryant G. Ly tport->releasing = false; 394688a678bbSBryant G. Ly 394788a678bbSBryant G. Ly rc = core_tpg_register(&tport->tport_wwn, &tport->se_tpg, 394888a678bbSBryant G. Ly tport->tport_proto_id); 394988a678bbSBryant G. Ly if (rc) 395088a678bbSBryant G. Ly return ERR_PTR(rc); 395188a678bbSBryant G. Ly 395288a678bbSBryant G. Ly return &tport->se_tpg; 395388a678bbSBryant G. Ly } 395488a678bbSBryant G. Ly 395588a678bbSBryant G. Ly static void ibmvscsis_drop_tpg(struct se_portal_group *se_tpg) 395688a678bbSBryant G. Ly { 395788a678bbSBryant G. Ly struct ibmvscsis_tport *tport = container_of(se_tpg, 395888a678bbSBryant G. Ly struct ibmvscsis_tport, 395988a678bbSBryant G. Ly se_tpg); 396088a678bbSBryant G. Ly 396188a678bbSBryant G. Ly tport->releasing = true; 396288a678bbSBryant G. Ly tport->enabled = false; 396388a678bbSBryant G. Ly 396488a678bbSBryant G. Ly /* 396588a678bbSBryant G. Ly * Release the virtual I_T Nexus for this ibmvscsis TPG 396688a678bbSBryant G. Ly */ 396788a678bbSBryant G. Ly ibmvscsis_drop_nexus(tport); 396888a678bbSBryant G. Ly /* 396988a678bbSBryant G. Ly * Deregister the se_tpg from TCM.. 397088a678bbSBryant G. Ly */ 397188a678bbSBryant G. Ly core_tpg_deregister(se_tpg); 397288a678bbSBryant G. Ly } 397388a678bbSBryant G. Ly 397488a678bbSBryant G. Ly static ssize_t ibmvscsis_wwn_version_show(struct config_item *item, 397588a678bbSBryant G. Ly char *page) 397688a678bbSBryant G. Ly { 397788a678bbSBryant G. Ly return scnprintf(page, PAGE_SIZE, "%s\n", IBMVSCSIS_VERSION); 397888a678bbSBryant G. Ly } 397988a678bbSBryant G. Ly CONFIGFS_ATTR_RO(ibmvscsis_wwn_, version); 398088a678bbSBryant G. Ly 398188a678bbSBryant G. Ly static struct configfs_attribute *ibmvscsis_wwn_attrs[] = { 398288a678bbSBryant G. Ly &ibmvscsis_wwn_attr_version, 398388a678bbSBryant G. Ly NULL, 398488a678bbSBryant G. Ly }; 398588a678bbSBryant G. Ly 398688a678bbSBryant G. Ly static ssize_t ibmvscsis_tpg_enable_show(struct config_item *item, 398788a678bbSBryant G. Ly char *page) 398888a678bbSBryant G. Ly { 398988a678bbSBryant G. Ly struct se_portal_group *se_tpg = to_tpg(item); 399088a678bbSBryant G. Ly struct ibmvscsis_tport *tport = container_of(se_tpg, 399188a678bbSBryant G. Ly struct ibmvscsis_tport, 399288a678bbSBryant G. Ly se_tpg); 399388a678bbSBryant G. Ly 399488a678bbSBryant G. Ly return snprintf(page, PAGE_SIZE, "%d\n", (tport->enabled) ? 1 : 0); 399588a678bbSBryant G. Ly } 399688a678bbSBryant G. Ly 399788a678bbSBryant G. Ly static ssize_t ibmvscsis_tpg_enable_store(struct config_item *item, 399888a678bbSBryant G. Ly const char *page, size_t count) 399988a678bbSBryant G. Ly { 400088a678bbSBryant G. Ly struct se_portal_group *se_tpg = to_tpg(item); 400188a678bbSBryant G. Ly struct ibmvscsis_tport *tport = container_of(se_tpg, 400288a678bbSBryant G. Ly struct ibmvscsis_tport, 400388a678bbSBryant G. Ly se_tpg); 400488a678bbSBryant G. Ly struct scsi_info *vscsi = container_of(tport, struct scsi_info, tport); 400588a678bbSBryant G. Ly unsigned long tmp; 400688a678bbSBryant G. Ly int rc; 400788a678bbSBryant G. Ly long lrc; 400888a678bbSBryant G. Ly 400988a678bbSBryant G. Ly rc = kstrtoul(page, 0, &tmp); 401088a678bbSBryant G. Ly if (rc < 0) { 4011417dff6cSBryant G. Ly dev_err(&vscsi->dev, "Unable to extract srpt_tpg_store_enable\n"); 401288a678bbSBryant G. Ly return -EINVAL; 401388a678bbSBryant G. Ly } 401488a678bbSBryant G. Ly 401588a678bbSBryant G. Ly if ((tmp != 0) && (tmp != 1)) { 4016417dff6cSBryant G. Ly dev_err(&vscsi->dev, "Illegal value for srpt_tpg_store_enable\n"); 401788a678bbSBryant G. Ly return -EINVAL; 401888a678bbSBryant G. Ly } 401988a678bbSBryant G. Ly 402088a678bbSBryant G. Ly if (tmp) { 402188a678bbSBryant G. Ly spin_lock_bh(&vscsi->intr_lock); 4022c9b3379fSMichael Cyr tport->enabled = true; 402388a678bbSBryant G. Ly lrc = ibmvscsis_enable_change_state(vscsi); 402488a678bbSBryant G. Ly if (lrc) 4025417dff6cSBryant G. Ly dev_err(&vscsi->dev, "enable_change_state failed, rc %ld state %d\n", 402688a678bbSBryant G. Ly lrc, vscsi->state); 402788a678bbSBryant G. Ly spin_unlock_bh(&vscsi->intr_lock); 402888a678bbSBryant G. Ly } else { 4029c9b3379fSMichael Cyr spin_lock_bh(&vscsi->intr_lock); 403088a678bbSBryant G. Ly tport->enabled = false; 4031c9b3379fSMichael Cyr /* This simulates the server going down */ 4032c9b3379fSMichael Cyr ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0); 4033c9b3379fSMichael Cyr spin_unlock_bh(&vscsi->intr_lock); 403488a678bbSBryant G. Ly } 403588a678bbSBryant G. Ly 4036417dff6cSBryant G. Ly dev_dbg(&vscsi->dev, "tpg_enable_store, tmp %ld, state %d\n", tmp, 4037417dff6cSBryant G. Ly vscsi->state); 403888a678bbSBryant G. Ly 403988a678bbSBryant G. Ly return count; 404088a678bbSBryant G. Ly } 404188a678bbSBryant G. Ly CONFIGFS_ATTR(ibmvscsis_tpg_, enable); 404288a678bbSBryant G. Ly 404388a678bbSBryant G. Ly static struct configfs_attribute *ibmvscsis_tpg_attrs[] = { 404488a678bbSBryant G. Ly &ibmvscsis_tpg_attr_enable, 404588a678bbSBryant G. Ly NULL, 404688a678bbSBryant G. Ly }; 404788a678bbSBryant G. Ly 404888a678bbSBryant G. Ly static const struct target_core_fabric_ops ibmvscsis_ops = { 404988a678bbSBryant G. Ly .module = THIS_MODULE, 405088a678bbSBryant G. Ly .name = "ibmvscsis", 4051b22bc278SBryant G. Ly .max_data_sg_nents = MAX_TXU / PAGE_SIZE, 405288a678bbSBryant G. Ly .get_fabric_name = ibmvscsis_get_fabric_name, 405388a678bbSBryant G. Ly .tpg_get_wwn = ibmvscsis_get_fabric_wwn, 405488a678bbSBryant G. Ly .tpg_get_tag = ibmvscsis_get_tag, 405588a678bbSBryant G. Ly .tpg_get_default_depth = ibmvscsis_get_default_depth, 405688a678bbSBryant G. Ly .tpg_check_demo_mode = ibmvscsis_check_true, 405788a678bbSBryant G. Ly .tpg_check_demo_mode_cache = ibmvscsis_check_true, 405888a678bbSBryant G. Ly .tpg_check_demo_mode_write_protect = ibmvscsis_check_false, 405988a678bbSBryant G. Ly .tpg_check_prod_mode_write_protect = ibmvscsis_check_false, 406088a678bbSBryant G. Ly .tpg_get_inst_index = ibmvscsis_tpg_get_inst_index, 406188a678bbSBryant G. Ly .check_stop_free = ibmvscsis_check_stop_free, 406288a678bbSBryant G. Ly .release_cmd = ibmvscsis_release_cmd, 406388a678bbSBryant G. Ly .sess_get_index = ibmvscsis_sess_get_index, 406488a678bbSBryant G. Ly .write_pending = ibmvscsis_write_pending, 406588a678bbSBryant G. Ly .write_pending_status = ibmvscsis_write_pending_status, 406688a678bbSBryant G. Ly .set_default_node_attributes = ibmvscsis_set_default_node_attrs, 406788a678bbSBryant G. Ly .get_cmd_state = ibmvscsis_get_cmd_state, 406888a678bbSBryant G. Ly .queue_data_in = ibmvscsis_queue_data_in, 406988a678bbSBryant G. Ly .queue_status = ibmvscsis_queue_status, 407088a678bbSBryant G. Ly .queue_tm_rsp = ibmvscsis_queue_tm_rsp, 407188a678bbSBryant G. Ly .aborted_task = ibmvscsis_aborted_task, 407288a678bbSBryant G. Ly /* 407388a678bbSBryant G. Ly * Setup function pointers for logic in target_core_fabric_configfs.c 407488a678bbSBryant G. Ly */ 407588a678bbSBryant G. Ly .fabric_make_wwn = ibmvscsis_make_tport, 407688a678bbSBryant G. Ly .fabric_drop_wwn = ibmvscsis_drop_tport, 407788a678bbSBryant G. Ly .fabric_make_tpg = ibmvscsis_make_tpg, 407888a678bbSBryant G. Ly .fabric_drop_tpg = ibmvscsis_drop_tpg, 407988a678bbSBryant G. Ly 408088a678bbSBryant G. Ly .tfc_wwn_attrs = ibmvscsis_wwn_attrs, 408188a678bbSBryant G. Ly .tfc_tpg_base_attrs = ibmvscsis_tpg_attrs, 408288a678bbSBryant G. Ly }; 408388a678bbSBryant G. Ly 408488a678bbSBryant G. Ly static void ibmvscsis_dev_release(struct device *dev) {}; 408588a678bbSBryant G. Ly 408688a678bbSBryant G. Ly static struct device_attribute dev_attr_system_id = 408788a678bbSBryant G. Ly __ATTR(system_id, S_IRUGO, system_id_show, NULL); 408888a678bbSBryant G. Ly 408988a678bbSBryant G. Ly static struct device_attribute dev_attr_partition_number = 409088a678bbSBryant G. Ly __ATTR(partition_number, S_IRUGO, partition_number_show, NULL); 409188a678bbSBryant G. Ly 409288a678bbSBryant G. Ly static struct device_attribute dev_attr_unit_address = 409388a678bbSBryant G. Ly __ATTR(unit_address, S_IRUGO, unit_address_show, NULL); 409488a678bbSBryant G. Ly 409588a678bbSBryant G. Ly static struct attribute *ibmvscsis_dev_attrs[] = { 409688a678bbSBryant G. Ly &dev_attr_system_id.attr, 409788a678bbSBryant G. Ly &dev_attr_partition_number.attr, 409888a678bbSBryant G. Ly &dev_attr_unit_address.attr, 409988a678bbSBryant G. Ly }; 410088a678bbSBryant G. Ly ATTRIBUTE_GROUPS(ibmvscsis_dev); 410188a678bbSBryant G. Ly 410288a678bbSBryant G. Ly static struct class ibmvscsis_class = { 410388a678bbSBryant G. Ly .name = "ibmvscsis", 410488a678bbSBryant G. Ly .dev_release = ibmvscsis_dev_release, 410588a678bbSBryant G. Ly .dev_groups = ibmvscsis_dev_groups, 410688a678bbSBryant G. Ly }; 410788a678bbSBryant G. Ly 4108e4df3eaaSArvind Yadav static const struct vio_device_id ibmvscsis_device_table[] = { 410988a678bbSBryant G. Ly { "v-scsi-host", "IBM,v-scsi-host" }, 411088a678bbSBryant G. Ly { "", "" } 411188a678bbSBryant G. Ly }; 411288a678bbSBryant G. Ly MODULE_DEVICE_TABLE(vio, ibmvscsis_device_table); 411388a678bbSBryant G. Ly 411488a678bbSBryant G. Ly static struct vio_driver ibmvscsis_driver = { 411588a678bbSBryant G. Ly .name = "ibmvscsis", 411688a678bbSBryant G. Ly .id_table = ibmvscsis_device_table, 411788a678bbSBryant G. Ly .probe = ibmvscsis_probe, 411888a678bbSBryant G. Ly .remove = ibmvscsis_remove, 411988a678bbSBryant G. Ly }; 412088a678bbSBryant G. Ly 412188a678bbSBryant G. Ly /* 412288a678bbSBryant G. Ly * ibmvscsis_init() - Kernel Module initialization 412388a678bbSBryant G. Ly * 412488a678bbSBryant G. Ly * Note: vio_register_driver() registers callback functions, and at least one 412588a678bbSBryant G. Ly * of those callback functions calls TCM - Linux IO Target Subsystem, thus 412688a678bbSBryant G. Ly * the SCSI Target template must be registered before vio_register_driver() 412788a678bbSBryant G. Ly * is called. 412888a678bbSBryant G. Ly */ 412988a678bbSBryant G. Ly static int __init ibmvscsis_init(void) 413088a678bbSBryant G. Ly { 413188a678bbSBryant G. Ly int rc = 0; 413288a678bbSBryant G. Ly 413388a678bbSBryant G. Ly rc = ibmvscsis_get_system_info(); 413488a678bbSBryant G. Ly if (rc) { 413588a678bbSBryant G. Ly pr_err("rc %d from get_system_info\n", rc); 413688a678bbSBryant G. Ly goto out; 413788a678bbSBryant G. Ly } 413888a678bbSBryant G. Ly 413988a678bbSBryant G. Ly rc = class_register(&ibmvscsis_class); 414088a678bbSBryant G. Ly if (rc) { 414188a678bbSBryant G. Ly pr_err("failed class register\n"); 414288a678bbSBryant G. Ly goto out; 414388a678bbSBryant G. Ly } 414488a678bbSBryant G. Ly 414588a678bbSBryant G. Ly rc = target_register_template(&ibmvscsis_ops); 414688a678bbSBryant G. Ly if (rc) { 414788a678bbSBryant G. Ly pr_err("rc %d from target_register_template\n", rc); 414888a678bbSBryant G. Ly goto unregister_class; 414988a678bbSBryant G. Ly } 415088a678bbSBryant G. Ly 415188a678bbSBryant G. Ly rc = vio_register_driver(&ibmvscsis_driver); 415288a678bbSBryant G. Ly if (rc) { 415388a678bbSBryant G. Ly pr_err("rc %d from vio_register_driver\n", rc); 415488a678bbSBryant G. Ly goto unregister_target; 415588a678bbSBryant G. Ly } 415688a678bbSBryant G. Ly 415788a678bbSBryant G. Ly return 0; 415888a678bbSBryant G. Ly 415988a678bbSBryant G. Ly unregister_target: 416088a678bbSBryant G. Ly target_unregister_template(&ibmvscsis_ops); 416188a678bbSBryant G. Ly unregister_class: 416288a678bbSBryant G. Ly class_unregister(&ibmvscsis_class); 416388a678bbSBryant G. Ly out: 416488a678bbSBryant G. Ly return rc; 416588a678bbSBryant G. Ly } 416688a678bbSBryant G. Ly 416788a678bbSBryant G. Ly static void __exit ibmvscsis_exit(void) 416888a678bbSBryant G. Ly { 416988a678bbSBryant G. Ly pr_info("Unregister IBM virtual SCSI host driver\n"); 417088a678bbSBryant G. Ly vio_unregister_driver(&ibmvscsis_driver); 417188a678bbSBryant G. Ly target_unregister_template(&ibmvscsis_ops); 417288a678bbSBryant G. Ly class_unregister(&ibmvscsis_class); 417388a678bbSBryant G. Ly } 417488a678bbSBryant G. Ly 417588a678bbSBryant G. Ly MODULE_DESCRIPTION("IBMVSCSIS fabric driver"); 417688a678bbSBryant G. Ly MODULE_AUTHOR("Bryant G. Ly and Michael Cyr"); 417788a678bbSBryant G. Ly MODULE_LICENSE("GPL"); 417888a678bbSBryant G. Ly MODULE_VERSION(IBMVSCSIS_VERSION); 417988a678bbSBryant G. Ly module_init(ibmvscsis_init); 418088a678bbSBryant G. Ly module_exit(ibmvscsis_exit); 4181