18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/******************************************************************************* 38c2ecf20Sopenharmony_ci * IBM Virtual SCSI Target Driver 48c2ecf20Sopenharmony_ci * Copyright (C) 2003-2005 Dave Boutcher (boutcher@us.ibm.com) IBM Corp. 58c2ecf20Sopenharmony_ci * Santiago Leon (santil@us.ibm.com) IBM Corp. 68c2ecf20Sopenharmony_ci * Linda Xie (lxie@us.ibm.com) IBM Corp. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2005-2011 FUJITA Tomonori <tomof@acm.org> 98c2ecf20Sopenharmony_ci * Copyright (C) 2010 Nicholas A. Bellinger <nab@kernel.org> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Authors: Bryant G. Ly <bryantly@linux.vnet.ibm.com> 128c2ecf20Sopenharmony_ci * Authors: Michael Cyr <mikecyr@linux.vnet.ibm.com> 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci ****************************************************************************/ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/types.h> 228c2ecf20Sopenharmony_ci#include <linux/list.h> 238c2ecf20Sopenharmony_ci#include <linux/string.h> 248c2ecf20Sopenharmony_ci#include <linux/delay.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <target/target_core_base.h> 278c2ecf20Sopenharmony_ci#include <target/target_core_fabric.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <asm/hvcall.h> 308c2ecf20Sopenharmony_ci#include <asm/vio.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <scsi/viosrp.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "ibmvscsi_tgt.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define IBMVSCSIS_VERSION "v0.2" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define INITIAL_SRP_LIMIT 1024 398c2ecf20Sopenharmony_ci#define DEFAULT_MAX_SECTORS 256 408c2ecf20Sopenharmony_ci#define MAX_TXU 1024 * 1024 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic uint max_vdma_size = MAX_H_COPY_RDMA; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic char system_id[SYS_ID_NAME_LEN] = ""; 458c2ecf20Sopenharmony_cistatic char partition_name[PARTITION_NAMELEN] = "UNKNOWN"; 468c2ecf20Sopenharmony_cistatic uint partition_number = -1; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* Adapter list and lock to control it */ 498c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(ibmvscsis_dev_lock); 508c2ecf20Sopenharmony_cistatic LIST_HEAD(ibmvscsis_dev_list); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic long ibmvscsis_parse_command(struct scsi_info *vscsi, 538c2ecf20Sopenharmony_ci struct viosrp_crq *crq); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void ibmvscsis_adapter_idle(struct scsi_info *vscsi); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void ibmvscsis_determine_resid(struct se_cmd *se_cmd, 588c2ecf20Sopenharmony_ci struct srp_rsp *rsp) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci u32 residual_count = se_cmd->residual_count; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (!residual_count) 638c2ecf20Sopenharmony_ci return; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) { 668c2ecf20Sopenharmony_ci if (se_cmd->data_direction == DMA_TO_DEVICE) { 678c2ecf20Sopenharmony_ci /* residual data from an underflow write */ 688c2ecf20Sopenharmony_ci rsp->flags = SRP_RSP_FLAG_DOUNDER; 698c2ecf20Sopenharmony_ci rsp->data_out_res_cnt = cpu_to_be32(residual_count); 708c2ecf20Sopenharmony_ci } else if (se_cmd->data_direction == DMA_FROM_DEVICE) { 718c2ecf20Sopenharmony_ci /* residual data from an underflow read */ 728c2ecf20Sopenharmony_ci rsp->flags = SRP_RSP_FLAG_DIUNDER; 738c2ecf20Sopenharmony_ci rsp->data_in_res_cnt = cpu_to_be32(residual_count); 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci } else if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) { 768c2ecf20Sopenharmony_ci if (se_cmd->data_direction == DMA_TO_DEVICE) { 778c2ecf20Sopenharmony_ci /* residual data from an overflow write */ 788c2ecf20Sopenharmony_ci rsp->flags = SRP_RSP_FLAG_DOOVER; 798c2ecf20Sopenharmony_ci rsp->data_out_res_cnt = cpu_to_be32(residual_count); 808c2ecf20Sopenharmony_ci } else if (se_cmd->data_direction == DMA_FROM_DEVICE) { 818c2ecf20Sopenharmony_ci /* residual data from an overflow read */ 828c2ecf20Sopenharmony_ci rsp->flags = SRP_RSP_FLAG_DIOVER; 838c2ecf20Sopenharmony_ci rsp->data_in_res_cnt = cpu_to_be32(residual_count); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/** 898c2ecf20Sopenharmony_ci * connection_broken() - Determine if the connection to the client is good 908c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * This function attempts to send a ping MAD to the client. If the call to 938c2ecf20Sopenharmony_ci * queue the request returns H_CLOSED then the connection has been broken 948c2ecf20Sopenharmony_ci * and the function returns TRUE. 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 978c2ecf20Sopenharmony_ci * Interrupt or Process environment 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_cistatic bool connection_broken(struct scsi_info *vscsi) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct viosrp_crq *crq; 1028c2ecf20Sopenharmony_ci u64 buffer[2] = { 0, 0 }; 1038c2ecf20Sopenharmony_ci long h_return_code; 1048c2ecf20Sopenharmony_ci bool rc = false; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* create a PING crq */ 1078c2ecf20Sopenharmony_ci crq = (struct viosrp_crq *)&buffer; 1088c2ecf20Sopenharmony_ci crq->valid = VALID_CMD_RESP_EL; 1098c2ecf20Sopenharmony_ci crq->format = MESSAGE_IN_CRQ; 1108c2ecf20Sopenharmony_ci crq->status = PING; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci h_return_code = h_send_crq(vscsi->dds.unit_id, 1138c2ecf20Sopenharmony_ci cpu_to_be64(buffer[MSG_HI]), 1148c2ecf20Sopenharmony_ci cpu_to_be64(buffer[MSG_LOW])); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Connection_broken: rc %ld\n", h_return_code); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (h_return_code == H_CLOSED) 1198c2ecf20Sopenharmony_ci rc = true; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return rc; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/** 1258c2ecf20Sopenharmony_ci * ibmvscsis_unregister_command_q() - Helper Function-Unregister Command Queue 1268c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 1278c2ecf20Sopenharmony_ci * 1288c2ecf20Sopenharmony_ci * This function calls h_free_q then frees the interrupt bit etc. 1298c2ecf20Sopenharmony_ci * It must release the lock before doing so because of the time it can take 1308c2ecf20Sopenharmony_ci * for h_free_crq in PHYP 1318c2ecf20Sopenharmony_ci * NOTE: the caller must make sure that state and or flags will prevent 1328c2ecf20Sopenharmony_ci * interrupt handler from scheduling work. 1338c2ecf20Sopenharmony_ci * NOTE: anyone calling this function may need to set the CRQ_CLOSED flag 1348c2ecf20Sopenharmony_ci * we can't do it here, because we don't have the lock 1358c2ecf20Sopenharmony_ci * 1368c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 1378c2ecf20Sopenharmony_ci * Process level 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_cistatic long ibmvscsis_unregister_command_q(struct scsi_info *vscsi) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci long qrc; 1428c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 1438c2ecf20Sopenharmony_ci int ticks = 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci do { 1468c2ecf20Sopenharmony_ci qrc = h_free_crq(vscsi->dds.unit_id); 1478c2ecf20Sopenharmony_ci switch (qrc) { 1488c2ecf20Sopenharmony_ci case H_SUCCESS: 1498c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 1508c2ecf20Sopenharmony_ci vscsi->flags &= ~PREP_FOR_SUSPEND_FLAGS; 1518c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci case H_HARDWARE: 1558c2ecf20Sopenharmony_ci case H_PARAMETER: 1568c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "unregister_command_q: error from h_free_crq %ld\n", 1578c2ecf20Sopenharmony_ci qrc); 1588c2ecf20Sopenharmony_ci rc = ERROR; 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci case H_BUSY: 1628c2ecf20Sopenharmony_ci case H_LONG_BUSY_ORDER_1_MSEC: 1638c2ecf20Sopenharmony_ci /* msleep not good for small values */ 1648c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 1658c2ecf20Sopenharmony_ci ticks += 1; 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci case H_LONG_BUSY_ORDER_10_MSEC: 1688c2ecf20Sopenharmony_ci usleep_range(10000, 20000); 1698c2ecf20Sopenharmony_ci ticks += 10; 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci case H_LONG_BUSY_ORDER_100_MSEC: 1728c2ecf20Sopenharmony_ci msleep(100); 1738c2ecf20Sopenharmony_ci ticks += 100; 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci case H_LONG_BUSY_ORDER_1_SEC: 1768c2ecf20Sopenharmony_ci ssleep(1); 1778c2ecf20Sopenharmony_ci ticks += 1000; 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci case H_LONG_BUSY_ORDER_10_SEC: 1808c2ecf20Sopenharmony_ci ssleep(10); 1818c2ecf20Sopenharmony_ci ticks += 10000; 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci case H_LONG_BUSY_ORDER_100_SEC: 1848c2ecf20Sopenharmony_ci ssleep(100); 1858c2ecf20Sopenharmony_ci ticks += 100000; 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci default: 1888c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "unregister_command_q: unknown error %ld from h_free_crq\n", 1898c2ecf20Sopenharmony_ci qrc); 1908c2ecf20Sopenharmony_ci rc = ERROR; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* 1958c2ecf20Sopenharmony_ci * dont wait more then 300 seconds 1968c2ecf20Sopenharmony_ci * ticks are in milliseconds more or less 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci if (ticks > 300000 && qrc != H_SUCCESS) { 1998c2ecf20Sopenharmony_ci rc = ERROR; 2008c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "Excessive wait for h_free_crq\n"); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci } while (qrc != H_SUCCESS && rc == ADAPT_SUCCESS); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Freeing CRQ: phyp rc %ld, rc %ld\n", qrc, rc); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return rc; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/** 2108c2ecf20Sopenharmony_ci * ibmvscsis_delete_client_info() - Helper function to Delete Client Info 2118c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 2128c2ecf20Sopenharmony_ci * @client_closed: True if client closed its queue 2138c2ecf20Sopenharmony_ci * 2148c2ecf20Sopenharmony_ci * Deletes information specific to the client when the client goes away 2158c2ecf20Sopenharmony_ci * 2168c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 2178c2ecf20Sopenharmony_ci * Interrupt or Process 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_cistatic void ibmvscsis_delete_client_info(struct scsi_info *vscsi, 2208c2ecf20Sopenharmony_ci bool client_closed) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci vscsi->client_cap = 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * Some things we don't want to clear if we're closing the queue, 2268c2ecf20Sopenharmony_ci * because some clients don't resend the host handshake when they 2278c2ecf20Sopenharmony_ci * get a transport event. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci if (client_closed) 2308c2ecf20Sopenharmony_ci vscsi->client_data.os_type = 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/** 2348c2ecf20Sopenharmony_ci * ibmvscsis_free_command_q() - Free Command Queue 2358c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 2368c2ecf20Sopenharmony_ci * 2378c2ecf20Sopenharmony_ci * This function calls unregister_command_q, then clears interrupts and 2388c2ecf20Sopenharmony_ci * any pending interrupt acknowledgments associated with the command q. 2398c2ecf20Sopenharmony_ci * It also clears memory if there is no error. 2408c2ecf20Sopenharmony_ci * 2418c2ecf20Sopenharmony_ci * PHYP did not meet the PAPR architecture so that we must give up the 2428c2ecf20Sopenharmony_ci * lock. This causes a timing hole regarding state change. To close the 2438c2ecf20Sopenharmony_ci * hole this routine does accounting on any change that occurred during 2448c2ecf20Sopenharmony_ci * the time the lock is not held. 2458c2ecf20Sopenharmony_ci * NOTE: must give up and then acquire the interrupt lock, the caller must 2468c2ecf20Sopenharmony_ci * make sure that state and or flags will prevent interrupt handler from 2478c2ecf20Sopenharmony_ci * scheduling work. 2488c2ecf20Sopenharmony_ci * 2498c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 2508c2ecf20Sopenharmony_ci * Process level, interrupt lock is held 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_cistatic long ibmvscsis_free_command_q(struct scsi_info *vscsi) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci int bytes; 2558c2ecf20Sopenharmony_ci u32 flags_under_lock; 2568c2ecf20Sopenharmony_ci u16 state_under_lock; 2578c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!(vscsi->flags & CRQ_CLOSED)) { 2608c2ecf20Sopenharmony_ci vio_disable_interrupts(vscsi->dma_dev); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci state_under_lock = vscsi->new_state; 2638c2ecf20Sopenharmony_ci flags_under_lock = vscsi->flags; 2648c2ecf20Sopenharmony_ci vscsi->phyp_acr_state = 0; 2658c2ecf20Sopenharmony_ci vscsi->phyp_acr_flags = 0; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 2688c2ecf20Sopenharmony_ci rc = ibmvscsis_unregister_command_q(vscsi); 2698c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (state_under_lock != vscsi->new_state) 2728c2ecf20Sopenharmony_ci vscsi->phyp_acr_state = vscsi->new_state; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci vscsi->phyp_acr_flags = ((~flags_under_lock) & vscsi->flags); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (rc == ADAPT_SUCCESS) { 2778c2ecf20Sopenharmony_ci bytes = vscsi->cmd_q.size * PAGE_SIZE; 2788c2ecf20Sopenharmony_ci memset(vscsi->cmd_q.base_addr, 0, bytes); 2798c2ecf20Sopenharmony_ci vscsi->cmd_q.index = 0; 2808c2ecf20Sopenharmony_ci vscsi->flags |= CRQ_CLOSED; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ibmvscsis_delete_client_info(vscsi, false); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "free_command_q: flags 0x%x, state 0x%hx, acr_flags 0x%x, acr_state 0x%hx\n", 2868c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state, vscsi->phyp_acr_flags, 2878c2ecf20Sopenharmony_ci vscsi->phyp_acr_state); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci return rc; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/** 2938c2ecf20Sopenharmony_ci * ibmvscsis_cmd_q_dequeue() - Get valid Command element 2948c2ecf20Sopenharmony_ci * @mask: Mask to use in case index wraps 2958c2ecf20Sopenharmony_ci * @current_index: Current index into command queue 2968c2ecf20Sopenharmony_ci * @base_addr: Pointer to start of command queue 2978c2ecf20Sopenharmony_ci * 2988c2ecf20Sopenharmony_ci * Returns a pointer to a valid command element or NULL, if the command 2998c2ecf20Sopenharmony_ci * queue is empty 3008c2ecf20Sopenharmony_ci * 3018c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 3028c2ecf20Sopenharmony_ci * Interrupt environment, interrupt lock held 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_cistatic struct viosrp_crq *ibmvscsis_cmd_q_dequeue(uint mask, 3058c2ecf20Sopenharmony_ci uint *current_index, 3068c2ecf20Sopenharmony_ci struct viosrp_crq *base_addr) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct viosrp_crq *ptr; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci ptr = base_addr + *current_index; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (ptr->valid) { 3138c2ecf20Sopenharmony_ci *current_index = (*current_index + 1) & mask; 3148c2ecf20Sopenharmony_ci dma_rmb(); 3158c2ecf20Sopenharmony_ci } else { 3168c2ecf20Sopenharmony_ci ptr = NULL; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return ptr; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci/** 3238c2ecf20Sopenharmony_ci * ibmvscsis_send_init_message() - send initialize message to the client 3248c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 3258c2ecf20Sopenharmony_ci * @format: Which Init Message format to send 3268c2ecf20Sopenharmony_ci * 3278c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 3288c2ecf20Sopenharmony_ci * Interrupt environment interrupt lock held 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_cistatic long ibmvscsis_send_init_message(struct scsi_info *vscsi, u8 format) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct viosrp_crq *crq; 3338c2ecf20Sopenharmony_ci u64 buffer[2] = { 0, 0 }; 3348c2ecf20Sopenharmony_ci long rc; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci crq = (struct viosrp_crq *)&buffer; 3378c2ecf20Sopenharmony_ci crq->valid = VALID_INIT_MSG; 3388c2ecf20Sopenharmony_ci crq->format = format; 3398c2ecf20Sopenharmony_ci rc = h_send_crq(vscsi->dds.unit_id, cpu_to_be64(buffer[MSG_HI]), 3408c2ecf20Sopenharmony_ci cpu_to_be64(buffer[MSG_LOW])); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return rc; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci/** 3468c2ecf20Sopenharmony_ci * ibmvscsis_check_init_msg() - Check init message valid 3478c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 3488c2ecf20Sopenharmony_ci * @format: Pointer to return format of Init Message, if any. 3498c2ecf20Sopenharmony_ci * Set to UNUSED_FORMAT if no Init Message in queue. 3508c2ecf20Sopenharmony_ci * 3518c2ecf20Sopenharmony_ci * Checks if an initialize message was queued by the initiatior 3528c2ecf20Sopenharmony_ci * after the queue was created and before the interrupt was enabled. 3538c2ecf20Sopenharmony_ci * 3548c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 3558c2ecf20Sopenharmony_ci * Process level only, interrupt lock held 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_cistatic long ibmvscsis_check_init_msg(struct scsi_info *vscsi, uint *format) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct viosrp_crq *crq; 3608c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci crq = ibmvscsis_cmd_q_dequeue(vscsi->cmd_q.mask, &vscsi->cmd_q.index, 3638c2ecf20Sopenharmony_ci vscsi->cmd_q.base_addr); 3648c2ecf20Sopenharmony_ci if (!crq) { 3658c2ecf20Sopenharmony_ci *format = (uint)UNUSED_FORMAT; 3668c2ecf20Sopenharmony_ci } else if (crq->valid == VALID_INIT_MSG && crq->format == INIT_MSG) { 3678c2ecf20Sopenharmony_ci *format = (uint)INIT_MSG; 3688c2ecf20Sopenharmony_ci crq->valid = INVALIDATE_CMD_RESP_EL; 3698c2ecf20Sopenharmony_ci dma_rmb(); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* 3728c2ecf20Sopenharmony_ci * the caller has ensured no initialize message was 3738c2ecf20Sopenharmony_ci * sent after the queue was 3748c2ecf20Sopenharmony_ci * created so there should be no other message on the queue. 3758c2ecf20Sopenharmony_ci */ 3768c2ecf20Sopenharmony_ci crq = ibmvscsis_cmd_q_dequeue(vscsi->cmd_q.mask, 3778c2ecf20Sopenharmony_ci &vscsi->cmd_q.index, 3788c2ecf20Sopenharmony_ci vscsi->cmd_q.base_addr); 3798c2ecf20Sopenharmony_ci if (crq) { 3808c2ecf20Sopenharmony_ci *format = (uint)(crq->format); 3818c2ecf20Sopenharmony_ci rc = ERROR; 3828c2ecf20Sopenharmony_ci crq->valid = INVALIDATE_CMD_RESP_EL; 3838c2ecf20Sopenharmony_ci dma_rmb(); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci } else { 3868c2ecf20Sopenharmony_ci *format = (uint)(crq->format); 3878c2ecf20Sopenharmony_ci rc = ERROR; 3888c2ecf20Sopenharmony_ci crq->valid = INVALIDATE_CMD_RESP_EL; 3898c2ecf20Sopenharmony_ci dma_rmb(); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return rc; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/** 3968c2ecf20Sopenharmony_ci * ibmvscsis_disconnect() - Helper function to disconnect 3978c2ecf20Sopenharmony_ci * @work: Pointer to work_struct, gives access to our adapter structure 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * An error has occurred or the driver received a Transport event, 4008c2ecf20Sopenharmony_ci * and the driver is requesting that the command queue be de-registered 4018c2ecf20Sopenharmony_ci * in a safe manner. If there is no outstanding I/O then we can stop the 4028c2ecf20Sopenharmony_ci * queue. If we are restarting the queue it will be reflected in the 4038c2ecf20Sopenharmony_ci * the state of the adapter. 4048c2ecf20Sopenharmony_ci * 4058c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 4068c2ecf20Sopenharmony_ci * Process environment 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_cistatic void ibmvscsis_disconnect(struct work_struct *work) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct scsi_info *vscsi = container_of(work, struct scsi_info, 4118c2ecf20Sopenharmony_ci proc_work); 4128c2ecf20Sopenharmony_ci u16 new_state; 4138c2ecf20Sopenharmony_ci bool wait_idle = false; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 4168c2ecf20Sopenharmony_ci new_state = vscsi->new_state; 4178c2ecf20Sopenharmony_ci vscsi->new_state = 0; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci vscsi->flags |= DISCONNECT_SCHEDULED; 4208c2ecf20Sopenharmony_ci vscsi->flags &= ~SCHEDULE_DISCONNECT; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "disconnect: flags 0x%x, state 0x%hx\n", 4238c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* 4268c2ecf20Sopenharmony_ci * check which state we are in and see if we 4278c2ecf20Sopenharmony_ci * should transitition to the new state 4288c2ecf20Sopenharmony_ci */ 4298c2ecf20Sopenharmony_ci switch (vscsi->state) { 4308c2ecf20Sopenharmony_ci /* Should never be called while in this state. */ 4318c2ecf20Sopenharmony_ci case NO_QUEUE: 4328c2ecf20Sopenharmony_ci /* 4338c2ecf20Sopenharmony_ci * Can never transition from this state; 4348c2ecf20Sopenharmony_ci * igonore errors and logout. 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_ci case UNCONFIGURING: 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* can transition from this state to UNCONFIGURING */ 4408c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 4418c2ecf20Sopenharmony_ci if (new_state == UNCONFIGURING) 4428c2ecf20Sopenharmony_ci vscsi->state = new_state; 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* 4468c2ecf20Sopenharmony_ci * Can transition from this state to to unconfiguring 4478c2ecf20Sopenharmony_ci * or err disconnect. 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ci case ERR_DISCONNECT_RECONNECT: 4508c2ecf20Sopenharmony_ci switch (new_state) { 4518c2ecf20Sopenharmony_ci case UNCONFIGURING: 4528c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 4538c2ecf20Sopenharmony_ci vscsi->state = new_state; 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci case WAIT_IDLE: 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci default: 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* can transition from this state to UNCONFIGURING */ 4648c2ecf20Sopenharmony_ci case ERR_DISCONNECTED: 4658c2ecf20Sopenharmony_ci if (new_state == UNCONFIGURING) 4668c2ecf20Sopenharmony_ci vscsi->state = new_state; 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci case WAIT_ENABLED: 4708c2ecf20Sopenharmony_ci switch (new_state) { 4718c2ecf20Sopenharmony_ci case UNCONFIGURING: 4728c2ecf20Sopenharmony_ci vscsi->state = new_state; 4738c2ecf20Sopenharmony_ci vscsi->flags |= RESPONSE_Q_DOWN; 4748c2ecf20Sopenharmony_ci vscsi->flags &= ~(SCHEDULE_DISCONNECT | 4758c2ecf20Sopenharmony_ci DISCONNECT_SCHEDULED); 4768c2ecf20Sopenharmony_ci dma_rmb(); 4778c2ecf20Sopenharmony_ci if (vscsi->flags & CFG_SLEEPING) { 4788c2ecf20Sopenharmony_ci vscsi->flags &= ~CFG_SLEEPING; 4798c2ecf20Sopenharmony_ci complete(&vscsi->unconfig); 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* should never happen */ 4848c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 4858c2ecf20Sopenharmony_ci case ERR_DISCONNECT_RECONNECT: 4868c2ecf20Sopenharmony_ci case WAIT_IDLE: 4878c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "disconnect: invalid state %d for WAIT_IDLE\n", 4888c2ecf20Sopenharmony_ci vscsi->state); 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci break; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci case WAIT_IDLE: 4948c2ecf20Sopenharmony_ci switch (new_state) { 4958c2ecf20Sopenharmony_ci case UNCONFIGURING: 4968c2ecf20Sopenharmony_ci vscsi->flags |= RESPONSE_Q_DOWN; 4978c2ecf20Sopenharmony_ci vscsi->state = new_state; 4988c2ecf20Sopenharmony_ci vscsi->flags &= ~(SCHEDULE_DISCONNECT | 4998c2ecf20Sopenharmony_ci DISCONNECT_SCHEDULED); 5008c2ecf20Sopenharmony_ci ibmvscsis_free_command_q(vscsi); 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 5038c2ecf20Sopenharmony_ci case ERR_DISCONNECT_RECONNECT: 5048c2ecf20Sopenharmony_ci vscsi->state = new_state; 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* 5108c2ecf20Sopenharmony_ci * Initiator has not done a successful srp login 5118c2ecf20Sopenharmony_ci * or has done a successful srp logout ( adapter was not 5128c2ecf20Sopenharmony_ci * busy). In the first case there can be responses queued 5138c2ecf20Sopenharmony_ci * waiting for space on the initiators response queue (MAD) 5148c2ecf20Sopenharmony_ci * The second case the adapter is idle. Assume the worse case, 5158c2ecf20Sopenharmony_ci * i.e. the second case. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ci case WAIT_CONNECTION: 5188c2ecf20Sopenharmony_ci case CONNECTED: 5198c2ecf20Sopenharmony_ci case SRP_PROCESSING: 5208c2ecf20Sopenharmony_ci wait_idle = true; 5218c2ecf20Sopenharmony_ci vscsi->state = new_state; 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* can transition from this state to UNCONFIGURING */ 5258c2ecf20Sopenharmony_ci case UNDEFINED: 5268c2ecf20Sopenharmony_ci if (new_state == UNCONFIGURING) 5278c2ecf20Sopenharmony_ci vscsi->state = new_state; 5288c2ecf20Sopenharmony_ci break; 5298c2ecf20Sopenharmony_ci default: 5308c2ecf20Sopenharmony_ci break; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (wait_idle) { 5348c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "disconnect start wait, active %d, sched %d\n", 5358c2ecf20Sopenharmony_ci (int)list_empty(&vscsi->active_q), 5368c2ecf20Sopenharmony_ci (int)list_empty(&vscsi->schedule_q)); 5378c2ecf20Sopenharmony_ci if (!list_empty(&vscsi->active_q) || 5388c2ecf20Sopenharmony_ci !list_empty(&vscsi->schedule_q)) { 5398c2ecf20Sopenharmony_ci vscsi->flags |= WAIT_FOR_IDLE; 5408c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "disconnect flags 0x%x\n", 5418c2ecf20Sopenharmony_ci vscsi->flags); 5428c2ecf20Sopenharmony_ci /* 5438c2ecf20Sopenharmony_ci * This routine is can not be called with the interrupt 5448c2ecf20Sopenharmony_ci * lock held. 5458c2ecf20Sopenharmony_ci */ 5468c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 5478c2ecf20Sopenharmony_ci wait_for_completion(&vscsi->wait_idle); 5488c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "disconnect stop wait\n"); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci ibmvscsis_adapter_idle(vscsi); 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci/** 5598c2ecf20Sopenharmony_ci * ibmvscsis_post_disconnect() - Schedule the disconnect 5608c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 5618c2ecf20Sopenharmony_ci * @new_state: State to move to after disconnecting 5628c2ecf20Sopenharmony_ci * @flag_bits: Flags to turn on in adapter structure 5638c2ecf20Sopenharmony_ci * 5648c2ecf20Sopenharmony_ci * If it's already been scheduled, then see if we need to "upgrade" 5658c2ecf20Sopenharmony_ci * the new state (if the one passed in is more "severe" than the 5668c2ecf20Sopenharmony_ci * previous one). 5678c2ecf20Sopenharmony_ci * 5688c2ecf20Sopenharmony_ci * PRECONDITION: 5698c2ecf20Sopenharmony_ci * interrupt lock is held 5708c2ecf20Sopenharmony_ci */ 5718c2ecf20Sopenharmony_cistatic void ibmvscsis_post_disconnect(struct scsi_info *vscsi, uint new_state, 5728c2ecf20Sopenharmony_ci uint flag_bits) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci uint state; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* check the validity of the new state */ 5778c2ecf20Sopenharmony_ci switch (new_state) { 5788c2ecf20Sopenharmony_ci case UNCONFIGURING: 5798c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 5808c2ecf20Sopenharmony_ci case ERR_DISCONNECT_RECONNECT: 5818c2ecf20Sopenharmony_ci case WAIT_IDLE: 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci default: 5858c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "post_disconnect: Invalid new state %d\n", 5868c2ecf20Sopenharmony_ci new_state); 5878c2ecf20Sopenharmony_ci return; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci vscsi->flags |= flag_bits; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "post_disconnect: new_state 0x%x, flag_bits 0x%x, vscsi->flags 0x%x, state %hx\n", 5938c2ecf20Sopenharmony_ci new_state, flag_bits, vscsi->flags, vscsi->state); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (!(vscsi->flags & (DISCONNECT_SCHEDULED | SCHEDULE_DISCONNECT))) { 5968c2ecf20Sopenharmony_ci vscsi->flags |= SCHEDULE_DISCONNECT; 5978c2ecf20Sopenharmony_ci vscsi->new_state = new_state; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci INIT_WORK(&vscsi->proc_work, ibmvscsis_disconnect); 6008c2ecf20Sopenharmony_ci (void)queue_work(vscsi->work_q, &vscsi->proc_work); 6018c2ecf20Sopenharmony_ci } else { 6028c2ecf20Sopenharmony_ci if (vscsi->new_state) 6038c2ecf20Sopenharmony_ci state = vscsi->new_state; 6048c2ecf20Sopenharmony_ci else 6058c2ecf20Sopenharmony_ci state = vscsi->state; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci switch (state) { 6088c2ecf20Sopenharmony_ci case NO_QUEUE: 6098c2ecf20Sopenharmony_ci case UNCONFIGURING: 6108c2ecf20Sopenharmony_ci break; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci case ERR_DISCONNECTED: 6138c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 6148c2ecf20Sopenharmony_ci case UNDEFINED: 6158c2ecf20Sopenharmony_ci if (new_state == UNCONFIGURING) 6168c2ecf20Sopenharmony_ci vscsi->new_state = new_state; 6178c2ecf20Sopenharmony_ci break; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci case ERR_DISCONNECT_RECONNECT: 6208c2ecf20Sopenharmony_ci switch (new_state) { 6218c2ecf20Sopenharmony_ci case UNCONFIGURING: 6228c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 6238c2ecf20Sopenharmony_ci vscsi->new_state = new_state; 6248c2ecf20Sopenharmony_ci break; 6258c2ecf20Sopenharmony_ci default: 6268c2ecf20Sopenharmony_ci break; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci case WAIT_ENABLED: 6318c2ecf20Sopenharmony_ci case WAIT_IDLE: 6328c2ecf20Sopenharmony_ci case WAIT_CONNECTION: 6338c2ecf20Sopenharmony_ci case CONNECTED: 6348c2ecf20Sopenharmony_ci case SRP_PROCESSING: 6358c2ecf20Sopenharmony_ci vscsi->new_state = new_state; 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci default: 6398c2ecf20Sopenharmony_ci break; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Leaving post_disconnect: flags 0x%x, new_state 0x%x\n", 6448c2ecf20Sopenharmony_ci vscsi->flags, vscsi->new_state); 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci/** 6488c2ecf20Sopenharmony_ci * ibmvscsis_handle_init_compl_msg() - Respond to an Init Complete Message 6498c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 6508c2ecf20Sopenharmony_ci * 6518c2ecf20Sopenharmony_ci * Must be called with interrupt lock held. 6528c2ecf20Sopenharmony_ci */ 6538c2ecf20Sopenharmony_cistatic long ibmvscsis_handle_init_compl_msg(struct scsi_info *vscsi) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci switch (vscsi->state) { 6588c2ecf20Sopenharmony_ci case NO_QUEUE: 6598c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 6608c2ecf20Sopenharmony_ci case ERR_DISCONNECT_RECONNECT: 6618c2ecf20Sopenharmony_ci case ERR_DISCONNECTED: 6628c2ecf20Sopenharmony_ci case UNCONFIGURING: 6638c2ecf20Sopenharmony_ci case UNDEFINED: 6648c2ecf20Sopenharmony_ci rc = ERROR; 6658c2ecf20Sopenharmony_ci break; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci case WAIT_CONNECTION: 6688c2ecf20Sopenharmony_ci vscsi->state = CONNECTED; 6698c2ecf20Sopenharmony_ci break; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci case WAIT_IDLE: 6728c2ecf20Sopenharmony_ci case SRP_PROCESSING: 6738c2ecf20Sopenharmony_ci case CONNECTED: 6748c2ecf20Sopenharmony_ci case WAIT_ENABLED: 6758c2ecf20Sopenharmony_ci default: 6768c2ecf20Sopenharmony_ci rc = ERROR; 6778c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "init_msg: invalid state %d to get init compl msg\n", 6788c2ecf20Sopenharmony_ci vscsi->state); 6798c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return rc; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci/** 6878c2ecf20Sopenharmony_ci * ibmvscsis_handle_init_msg() - Respond to an Init Message 6888c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 6898c2ecf20Sopenharmony_ci * 6908c2ecf20Sopenharmony_ci * Must be called with interrupt lock held. 6918c2ecf20Sopenharmony_ci */ 6928c2ecf20Sopenharmony_cistatic long ibmvscsis_handle_init_msg(struct scsi_info *vscsi) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci switch (vscsi->state) { 6978c2ecf20Sopenharmony_ci case WAIT_CONNECTION: 6988c2ecf20Sopenharmony_ci rc = ibmvscsis_send_init_message(vscsi, INIT_COMPLETE_MSG); 6998c2ecf20Sopenharmony_ci switch (rc) { 7008c2ecf20Sopenharmony_ci case H_SUCCESS: 7018c2ecf20Sopenharmony_ci vscsi->state = CONNECTED; 7028c2ecf20Sopenharmony_ci break; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci case H_PARAMETER: 7058c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "init_msg: failed to send, rc %ld\n", 7068c2ecf20Sopenharmony_ci rc); 7078c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0); 7088c2ecf20Sopenharmony_ci break; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci case H_DROPPED: 7118c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "init_msg: failed to send, rc %ld\n", 7128c2ecf20Sopenharmony_ci rc); 7138c2ecf20Sopenharmony_ci rc = ERROR; 7148c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, 7158c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT, 0); 7168c2ecf20Sopenharmony_ci break; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci case H_CLOSED: 7198c2ecf20Sopenharmony_ci dev_warn(&vscsi->dev, "init_msg: failed to send, rc %ld\n", 7208c2ecf20Sopenharmony_ci rc); 7218c2ecf20Sopenharmony_ci rc = 0; 7228c2ecf20Sopenharmony_ci break; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci case UNDEFINED: 7278c2ecf20Sopenharmony_ci rc = ERROR; 7288c2ecf20Sopenharmony_ci break; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci case UNCONFIGURING: 7318c2ecf20Sopenharmony_ci break; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci case WAIT_ENABLED: 7348c2ecf20Sopenharmony_ci case CONNECTED: 7358c2ecf20Sopenharmony_ci case SRP_PROCESSING: 7368c2ecf20Sopenharmony_ci case WAIT_IDLE: 7378c2ecf20Sopenharmony_ci case NO_QUEUE: 7388c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 7398c2ecf20Sopenharmony_ci case ERR_DISCONNECT_RECONNECT: 7408c2ecf20Sopenharmony_ci case ERR_DISCONNECTED: 7418c2ecf20Sopenharmony_ci default: 7428c2ecf20Sopenharmony_ci rc = ERROR; 7438c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "init_msg: invalid state %d to get init msg\n", 7448c2ecf20Sopenharmony_ci vscsi->state); 7458c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 7468c2ecf20Sopenharmony_ci break; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci return rc; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci/** 7538c2ecf20Sopenharmony_ci * ibmvscsis_init_msg() - Respond to an init message 7548c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 7558c2ecf20Sopenharmony_ci * @crq: Pointer to CRQ element containing the Init Message 7568c2ecf20Sopenharmony_ci * 7578c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 7588c2ecf20Sopenharmony_ci * Interrupt, interrupt lock held 7598c2ecf20Sopenharmony_ci */ 7608c2ecf20Sopenharmony_cistatic long ibmvscsis_init_msg(struct scsi_info *vscsi, struct viosrp_crq *crq) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "init_msg: state 0x%hx\n", vscsi->state); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci rc = h_vioctl(vscsi->dds.unit_id, H_GET_PARTNER_INFO, 7678c2ecf20Sopenharmony_ci (u64)vscsi->map_ioba | ((u64)PAGE_SIZE << 32), 0, 0, 0, 7688c2ecf20Sopenharmony_ci 0); 7698c2ecf20Sopenharmony_ci if (rc == H_SUCCESS) { 7708c2ecf20Sopenharmony_ci vscsi->client_data.partition_number = 7718c2ecf20Sopenharmony_ci be64_to_cpu(*(u64 *)vscsi->map_buf); 7728c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "init_msg, part num %d\n", 7738c2ecf20Sopenharmony_ci vscsi->client_data.partition_number); 7748c2ecf20Sopenharmony_ci } else { 7758c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "init_msg h_vioctl rc %ld\n", rc); 7768c2ecf20Sopenharmony_ci rc = ADAPT_SUCCESS; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (crq->format == INIT_MSG) { 7808c2ecf20Sopenharmony_ci rc = ibmvscsis_handle_init_msg(vscsi); 7818c2ecf20Sopenharmony_ci } else if (crq->format == INIT_COMPLETE_MSG) { 7828c2ecf20Sopenharmony_ci rc = ibmvscsis_handle_init_compl_msg(vscsi); 7838c2ecf20Sopenharmony_ci } else { 7848c2ecf20Sopenharmony_ci rc = ERROR; 7858c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "init_msg: invalid format %d\n", 7868c2ecf20Sopenharmony_ci (uint)crq->format); 7878c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci return rc; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci/** 7948c2ecf20Sopenharmony_ci * ibmvscsis_establish_new_q() - Establish new CRQ queue 7958c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 7968c2ecf20Sopenharmony_ci * 7978c2ecf20Sopenharmony_ci * Must be called with interrupt lock held. 7988c2ecf20Sopenharmony_ci */ 7998c2ecf20Sopenharmony_cistatic long ibmvscsis_establish_new_q(struct scsi_info *vscsi) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 8028c2ecf20Sopenharmony_ci uint format; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci rc = h_vioctl(vscsi->dds.unit_id, H_ENABLE_PREPARE_FOR_SUSPEND, 30000, 8058c2ecf20Sopenharmony_ci 0, 0, 0, 0); 8068c2ecf20Sopenharmony_ci if (rc == H_SUCCESS) 8078c2ecf20Sopenharmony_ci vscsi->flags |= PREP_FOR_SUSPEND_ENABLED; 8088c2ecf20Sopenharmony_ci else if (rc != H_NOT_FOUND) 8098c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "Error from Enable Prepare for Suspend: %ld\n", 8108c2ecf20Sopenharmony_ci rc); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci vscsi->flags &= PRESERVE_FLAG_FIELDS; 8138c2ecf20Sopenharmony_ci vscsi->rsp_q_timer.timer_pops = 0; 8148c2ecf20Sopenharmony_ci vscsi->debit = 0; 8158c2ecf20Sopenharmony_ci vscsi->credit = 0; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci rc = vio_enable_interrupts(vscsi->dma_dev); 8188c2ecf20Sopenharmony_ci if (rc) { 8198c2ecf20Sopenharmony_ci dev_warn(&vscsi->dev, "establish_new_q: failed to enable interrupts, rc %ld\n", 8208c2ecf20Sopenharmony_ci rc); 8218c2ecf20Sopenharmony_ci return rc; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci rc = ibmvscsis_check_init_msg(vscsi, &format); 8258c2ecf20Sopenharmony_ci if (rc) { 8268c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "establish_new_q: check_init_msg failed, rc %ld\n", 8278c2ecf20Sopenharmony_ci rc); 8288c2ecf20Sopenharmony_ci return rc; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (format == UNUSED_FORMAT) { 8328c2ecf20Sopenharmony_ci rc = ibmvscsis_send_init_message(vscsi, INIT_MSG); 8338c2ecf20Sopenharmony_ci switch (rc) { 8348c2ecf20Sopenharmony_ci case H_SUCCESS: 8358c2ecf20Sopenharmony_ci case H_DROPPED: 8368c2ecf20Sopenharmony_ci case H_CLOSED: 8378c2ecf20Sopenharmony_ci rc = ADAPT_SUCCESS; 8388c2ecf20Sopenharmony_ci break; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci case H_PARAMETER: 8418c2ecf20Sopenharmony_ci case H_HARDWARE: 8428c2ecf20Sopenharmony_ci break; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci default: 8458c2ecf20Sopenharmony_ci vscsi->state = UNDEFINED; 8468c2ecf20Sopenharmony_ci rc = H_HARDWARE; 8478c2ecf20Sopenharmony_ci break; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci } else if (format == INIT_MSG) { 8508c2ecf20Sopenharmony_ci rc = ibmvscsis_handle_init_msg(vscsi); 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci return rc; 8548c2ecf20Sopenharmony_ci} 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci/** 8578c2ecf20Sopenharmony_ci * ibmvscsis_reset_queue() - Reset CRQ Queue 8588c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 8598c2ecf20Sopenharmony_ci * 8608c2ecf20Sopenharmony_ci * This function calls h_free_q and then calls h_reg_q and does all 8618c2ecf20Sopenharmony_ci * of the bookkeeping to get us back to where we can communicate. 8628c2ecf20Sopenharmony_ci * 8638c2ecf20Sopenharmony_ci * Actually, we don't always call h_free_crq. A problem was discovered 8648c2ecf20Sopenharmony_ci * where one partition would close and reopen his queue, which would 8658c2ecf20Sopenharmony_ci * cause his partner to get a transport event, which would cause him to 8668c2ecf20Sopenharmony_ci * close and reopen his queue, which would cause the original partition 8678c2ecf20Sopenharmony_ci * to get a transport event, etc., etc. To prevent this, we don't 8688c2ecf20Sopenharmony_ci * actually close our queue if the client initiated the reset, (i.e. 8698c2ecf20Sopenharmony_ci * either we got a transport event or we have detected that the client's 8708c2ecf20Sopenharmony_ci * queue is gone) 8718c2ecf20Sopenharmony_ci * 8728c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 8738c2ecf20Sopenharmony_ci * Process environment, called with interrupt lock held 8748c2ecf20Sopenharmony_ci */ 8758c2ecf20Sopenharmony_cistatic void ibmvscsis_reset_queue(struct scsi_info *vscsi) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci int bytes; 8788c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "reset_queue: flags 0x%x\n", vscsi->flags); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* don't reset, the client did it for us */ 8838c2ecf20Sopenharmony_ci if (vscsi->flags & (CLIENT_FAILED | TRANS_EVENT)) { 8848c2ecf20Sopenharmony_ci vscsi->flags &= PRESERVE_FLAG_FIELDS; 8858c2ecf20Sopenharmony_ci vscsi->rsp_q_timer.timer_pops = 0; 8868c2ecf20Sopenharmony_ci vscsi->debit = 0; 8878c2ecf20Sopenharmony_ci vscsi->credit = 0; 8888c2ecf20Sopenharmony_ci vscsi->state = WAIT_CONNECTION; 8898c2ecf20Sopenharmony_ci vio_enable_interrupts(vscsi->dma_dev); 8908c2ecf20Sopenharmony_ci } else { 8918c2ecf20Sopenharmony_ci rc = ibmvscsis_free_command_q(vscsi); 8928c2ecf20Sopenharmony_ci if (rc == ADAPT_SUCCESS) { 8938c2ecf20Sopenharmony_ci vscsi->state = WAIT_CONNECTION; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci bytes = vscsi->cmd_q.size * PAGE_SIZE; 8968c2ecf20Sopenharmony_ci rc = h_reg_crq(vscsi->dds.unit_id, 8978c2ecf20Sopenharmony_ci vscsi->cmd_q.crq_token, bytes); 8988c2ecf20Sopenharmony_ci if (rc == H_CLOSED || rc == H_SUCCESS) { 8998c2ecf20Sopenharmony_ci rc = ibmvscsis_establish_new_q(vscsi); 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (rc != ADAPT_SUCCESS) { 9038c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "reset_queue: reg_crq rc %ld\n", 9048c2ecf20Sopenharmony_ci rc); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci vscsi->state = ERR_DISCONNECTED; 9078c2ecf20Sopenharmony_ci vscsi->flags |= RESPONSE_Q_DOWN; 9088c2ecf20Sopenharmony_ci ibmvscsis_free_command_q(vscsi); 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci } else { 9118c2ecf20Sopenharmony_ci vscsi->state = ERR_DISCONNECTED; 9128c2ecf20Sopenharmony_ci vscsi->flags |= RESPONSE_Q_DOWN; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci/** 9188c2ecf20Sopenharmony_ci * ibmvscsis_free_cmd_resources() - Free command resources 9198c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 9208c2ecf20Sopenharmony_ci * @cmd: Command which is not longer in use 9218c2ecf20Sopenharmony_ci * 9228c2ecf20Sopenharmony_ci * Must be called with interrupt lock held. 9238c2ecf20Sopenharmony_ci */ 9248c2ecf20Sopenharmony_cistatic void ibmvscsis_free_cmd_resources(struct scsi_info *vscsi, 9258c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci switch (cmd->type) { 9308c2ecf20Sopenharmony_ci case TASK_MANAGEMENT: 9318c2ecf20Sopenharmony_ci case SCSI_CDB: 9328c2ecf20Sopenharmony_ci /* 9338c2ecf20Sopenharmony_ci * When the queue goes down this value is cleared, so it 9348c2ecf20Sopenharmony_ci * cannot be cleared in this general purpose function. 9358c2ecf20Sopenharmony_ci */ 9368c2ecf20Sopenharmony_ci if (vscsi->debit) 9378c2ecf20Sopenharmony_ci vscsi->debit -= 1; 9388c2ecf20Sopenharmony_ci break; 9398c2ecf20Sopenharmony_ci case ADAPTER_MAD: 9408c2ecf20Sopenharmony_ci vscsi->flags &= ~PROCESSING_MAD; 9418c2ecf20Sopenharmony_ci break; 9428c2ecf20Sopenharmony_ci case UNSET_TYPE: 9438c2ecf20Sopenharmony_ci break; 9448c2ecf20Sopenharmony_ci default: 9458c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "free_cmd_resources unknown type %d\n", 9468c2ecf20Sopenharmony_ci cmd->type); 9478c2ecf20Sopenharmony_ci break; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci cmd->iue = NULL; 9518c2ecf20Sopenharmony_ci list_add_tail(&cmd->list, &vscsi->free_cmd); 9528c2ecf20Sopenharmony_ci srp_iu_put(iue); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (list_empty(&vscsi->active_q) && list_empty(&vscsi->schedule_q) && 9558c2ecf20Sopenharmony_ci list_empty(&vscsi->waiting_rsp) && (vscsi->flags & WAIT_FOR_IDLE)) { 9568c2ecf20Sopenharmony_ci vscsi->flags &= ~WAIT_FOR_IDLE; 9578c2ecf20Sopenharmony_ci complete(&vscsi->wait_idle); 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci/** 9628c2ecf20Sopenharmony_ci * ibmvscsis_ready_for_suspend() - Helper function to call VIOCTL 9638c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 9648c2ecf20Sopenharmony_ci * @idle: Indicates whether we were called from adapter_idle. This 9658c2ecf20Sopenharmony_ci * is important to know if we need to do a disconnect, since if 9668c2ecf20Sopenharmony_ci * we're called from adapter_idle, we're still processing the 9678c2ecf20Sopenharmony_ci * current disconnect, so we can't just call post_disconnect. 9688c2ecf20Sopenharmony_ci * 9698c2ecf20Sopenharmony_ci * This function is called when the adapter is idle when phyp has sent 9708c2ecf20Sopenharmony_ci * us a Prepare for Suspend Transport Event. 9718c2ecf20Sopenharmony_ci * 9728c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 9738c2ecf20Sopenharmony_ci * Process or interrupt environment called with interrupt lock held 9748c2ecf20Sopenharmony_ci */ 9758c2ecf20Sopenharmony_cistatic long ibmvscsis_ready_for_suspend(struct scsi_info *vscsi, bool idle) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci long rc = 0; 9788c2ecf20Sopenharmony_ci struct viosrp_crq *crq; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* See if there is a Resume event in the queue */ 9818c2ecf20Sopenharmony_ci crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "ready_suspend: flags 0x%x, state 0x%hx crq_valid:%x\n", 9848c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state, (int)crq->valid); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci if (!(vscsi->flags & PREP_FOR_SUSPEND_ABORTED) && !(crq->valid)) { 9878c2ecf20Sopenharmony_ci rc = h_vioctl(vscsi->dds.unit_id, H_READY_FOR_SUSPEND, 0, 0, 0, 9888c2ecf20Sopenharmony_ci 0, 0); 9898c2ecf20Sopenharmony_ci if (rc) { 9908c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "Ready for Suspend Vioctl failed: %ld\n", 9918c2ecf20Sopenharmony_ci rc); 9928c2ecf20Sopenharmony_ci rc = 0; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci } else if (((vscsi->flags & PREP_FOR_SUSPEND_OVERWRITE) && 9958c2ecf20Sopenharmony_ci (vscsi->flags & PREP_FOR_SUSPEND_ABORTED)) || 9968c2ecf20Sopenharmony_ci ((crq->valid) && ((crq->valid != VALID_TRANS_EVENT) || 9978c2ecf20Sopenharmony_ci (crq->format != RESUME_FROM_SUSP)))) { 9988c2ecf20Sopenharmony_ci if (idle) { 9998c2ecf20Sopenharmony_ci vscsi->state = ERR_DISCONNECT_RECONNECT; 10008c2ecf20Sopenharmony_ci ibmvscsis_reset_queue(vscsi); 10018c2ecf20Sopenharmony_ci rc = -1; 10028c2ecf20Sopenharmony_ci } else if (vscsi->state == CONNECTED) { 10038c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, 10048c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT, 0); 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci if ((crq->valid) && ((crq->valid != VALID_TRANS_EVENT) || 10108c2ecf20Sopenharmony_ci (crq->format != RESUME_FROM_SUSP))) 10118c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "Invalid element in CRQ after Prepare for Suspend"); 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci vscsi->flags &= ~(PREP_FOR_SUSPEND_PENDING | PREP_FOR_SUSPEND_ABORTED); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci return rc; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci/** 10208c2ecf20Sopenharmony_ci * ibmvscsis_trans_event() - Handle a Transport Event 10218c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 10228c2ecf20Sopenharmony_ci * @crq: Pointer to CRQ entry containing the Transport Event 10238c2ecf20Sopenharmony_ci * 10248c2ecf20Sopenharmony_ci * Do the logic to close the I_T nexus. This function may not 10258c2ecf20Sopenharmony_ci * behave to specification. 10268c2ecf20Sopenharmony_ci * 10278c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 10288c2ecf20Sopenharmony_ci * Interrupt, interrupt lock held 10298c2ecf20Sopenharmony_ci */ 10308c2ecf20Sopenharmony_cistatic long ibmvscsis_trans_event(struct scsi_info *vscsi, 10318c2ecf20Sopenharmony_ci struct viosrp_crq *crq) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "trans_event: format %d, flags 0x%x, state 0x%hx\n", 10368c2ecf20Sopenharmony_ci (int)crq->format, vscsi->flags, vscsi->state); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci switch (crq->format) { 10398c2ecf20Sopenharmony_ci case MIGRATED: 10408c2ecf20Sopenharmony_ci case PARTNER_FAILED: 10418c2ecf20Sopenharmony_ci case PARTNER_DEREGISTER: 10428c2ecf20Sopenharmony_ci ibmvscsis_delete_client_info(vscsi, true); 10438c2ecf20Sopenharmony_ci if (crq->format == MIGRATED) 10448c2ecf20Sopenharmony_ci vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE; 10458c2ecf20Sopenharmony_ci switch (vscsi->state) { 10468c2ecf20Sopenharmony_ci case NO_QUEUE: 10478c2ecf20Sopenharmony_ci case ERR_DISCONNECTED: 10488c2ecf20Sopenharmony_ci case UNDEFINED: 10498c2ecf20Sopenharmony_ci break; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci case UNCONFIGURING: 10528c2ecf20Sopenharmony_ci vscsi->flags |= (RESPONSE_Q_DOWN | TRANS_EVENT); 10538c2ecf20Sopenharmony_ci break; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci case WAIT_ENABLED: 10568c2ecf20Sopenharmony_ci break; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci case WAIT_CONNECTION: 10598c2ecf20Sopenharmony_ci break; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci case CONNECTED: 10628c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 10638c2ecf20Sopenharmony_ci (RESPONSE_Q_DOWN | 10648c2ecf20Sopenharmony_ci TRANS_EVENT)); 10658c2ecf20Sopenharmony_ci break; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci case SRP_PROCESSING: 10688c2ecf20Sopenharmony_ci if ((vscsi->debit > 0) || 10698c2ecf20Sopenharmony_ci !list_empty(&vscsi->schedule_q) || 10708c2ecf20Sopenharmony_ci !list_empty(&vscsi->waiting_rsp) || 10718c2ecf20Sopenharmony_ci !list_empty(&vscsi->active_q)) { 10728c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "debit %d, sched %d, wait %d, active %d\n", 10738c2ecf20Sopenharmony_ci vscsi->debit, 10748c2ecf20Sopenharmony_ci (int)list_empty(&vscsi->schedule_q), 10758c2ecf20Sopenharmony_ci (int)list_empty(&vscsi->waiting_rsp), 10768c2ecf20Sopenharmony_ci (int)list_empty(&vscsi->active_q)); 10778c2ecf20Sopenharmony_ci dev_warn(&vscsi->dev, "connection lost with outstanding work\n"); 10788c2ecf20Sopenharmony_ci } else { 10798c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "trans_event: SRP Processing, but no outstanding work\n"); 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 10838c2ecf20Sopenharmony_ci (RESPONSE_Q_DOWN | 10848c2ecf20Sopenharmony_ci TRANS_EVENT)); 10858c2ecf20Sopenharmony_ci break; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 10888c2ecf20Sopenharmony_ci case ERR_DISCONNECT_RECONNECT: 10898c2ecf20Sopenharmony_ci case WAIT_IDLE: 10908c2ecf20Sopenharmony_ci vscsi->flags |= (RESPONSE_Q_DOWN | TRANS_EVENT); 10918c2ecf20Sopenharmony_ci break; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci break; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci case PREPARE_FOR_SUSPEND: 10968c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Prep for Suspend, crq status = 0x%x\n", 10978c2ecf20Sopenharmony_ci (int)crq->status); 10988c2ecf20Sopenharmony_ci switch (vscsi->state) { 10998c2ecf20Sopenharmony_ci case ERR_DISCONNECTED: 11008c2ecf20Sopenharmony_ci case WAIT_CONNECTION: 11018c2ecf20Sopenharmony_ci case CONNECTED: 11028c2ecf20Sopenharmony_ci ibmvscsis_ready_for_suspend(vscsi, false); 11038c2ecf20Sopenharmony_ci break; 11048c2ecf20Sopenharmony_ci case SRP_PROCESSING: 11058c2ecf20Sopenharmony_ci vscsi->resume_state = vscsi->state; 11068c2ecf20Sopenharmony_ci vscsi->flags |= PREP_FOR_SUSPEND_PENDING; 11078c2ecf20Sopenharmony_ci if (crq->status == CRQ_ENTRY_OVERWRITTEN) 11088c2ecf20Sopenharmony_ci vscsi->flags |= PREP_FOR_SUSPEND_OVERWRITE; 11098c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 0); 11108c2ecf20Sopenharmony_ci break; 11118c2ecf20Sopenharmony_ci case NO_QUEUE: 11128c2ecf20Sopenharmony_ci case UNDEFINED: 11138c2ecf20Sopenharmony_ci case UNCONFIGURING: 11148c2ecf20Sopenharmony_ci case WAIT_ENABLED: 11158c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 11168c2ecf20Sopenharmony_ci case ERR_DISCONNECT_RECONNECT: 11178c2ecf20Sopenharmony_ci case WAIT_IDLE: 11188c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "Invalid state for Prepare for Suspend Trans Event: 0x%x\n", 11198c2ecf20Sopenharmony_ci vscsi->state); 11208c2ecf20Sopenharmony_ci break; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci break; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci case RESUME_FROM_SUSP: 11258c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Resume from Suspend, crq status = 0x%x\n", 11268c2ecf20Sopenharmony_ci (int)crq->status); 11278c2ecf20Sopenharmony_ci if (vscsi->flags & PREP_FOR_SUSPEND_PENDING) { 11288c2ecf20Sopenharmony_ci vscsi->flags |= PREP_FOR_SUSPEND_ABORTED; 11298c2ecf20Sopenharmony_ci } else { 11308c2ecf20Sopenharmony_ci if ((crq->status == CRQ_ENTRY_OVERWRITTEN) || 11318c2ecf20Sopenharmony_ci (vscsi->flags & PREP_FOR_SUSPEND_OVERWRITE)) { 11328c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, 11338c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT, 11348c2ecf20Sopenharmony_ci 0); 11358c2ecf20Sopenharmony_ci vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE; 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci break; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci default: 11418c2ecf20Sopenharmony_ci rc = ERROR; 11428c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "trans_event: invalid format %d\n", 11438c2ecf20Sopenharmony_ci (uint)crq->format); 11448c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 11458c2ecf20Sopenharmony_ci RESPONSE_Q_DOWN); 11468c2ecf20Sopenharmony_ci break; 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci rc = vscsi->flags & SCHEDULE_DISCONNECT; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Leaving trans_event: flags 0x%x, state 0x%hx, rc %ld\n", 11528c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state, rc); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci return rc; 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci/** 11588c2ecf20Sopenharmony_ci * ibmvscsis_poll_cmd_q() - Poll Command Queue 11598c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 11608c2ecf20Sopenharmony_ci * 11618c2ecf20Sopenharmony_ci * Called to handle command elements that may have arrived while 11628c2ecf20Sopenharmony_ci * interrupts were disabled. 11638c2ecf20Sopenharmony_ci * 11648c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 11658c2ecf20Sopenharmony_ci * intr_lock must be held 11668c2ecf20Sopenharmony_ci */ 11678c2ecf20Sopenharmony_cistatic void ibmvscsis_poll_cmd_q(struct scsi_info *vscsi) 11688c2ecf20Sopenharmony_ci{ 11698c2ecf20Sopenharmony_ci struct viosrp_crq *crq; 11708c2ecf20Sopenharmony_ci long rc; 11718c2ecf20Sopenharmony_ci bool ack = true; 11728c2ecf20Sopenharmony_ci volatile u8 valid; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "poll_cmd_q: flags 0x%x, state 0x%hx, q index %ud\n", 11758c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state, vscsi->cmd_q.index); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci rc = vscsi->flags & SCHEDULE_DISCONNECT; 11788c2ecf20Sopenharmony_ci crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index; 11798c2ecf20Sopenharmony_ci valid = crq->valid; 11808c2ecf20Sopenharmony_ci dma_rmb(); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci while (valid) { 11838c2ecf20Sopenharmony_cipoll_work: 11848c2ecf20Sopenharmony_ci vscsi->cmd_q.index = 11858c2ecf20Sopenharmony_ci (vscsi->cmd_q.index + 1) & vscsi->cmd_q.mask; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci if (!rc) { 11888c2ecf20Sopenharmony_ci rc = ibmvscsis_parse_command(vscsi, crq); 11898c2ecf20Sopenharmony_ci } else { 11908c2ecf20Sopenharmony_ci if ((uint)crq->valid == VALID_TRANS_EVENT) { 11918c2ecf20Sopenharmony_ci /* 11928c2ecf20Sopenharmony_ci * must service the transport layer events even 11938c2ecf20Sopenharmony_ci * in an error state, dont break out until all 11948c2ecf20Sopenharmony_ci * the consecutive transport events have been 11958c2ecf20Sopenharmony_ci * processed 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_ci rc = ibmvscsis_trans_event(vscsi, crq); 11988c2ecf20Sopenharmony_ci } else if (vscsi->flags & TRANS_EVENT) { 11998c2ecf20Sopenharmony_ci /* 12008c2ecf20Sopenharmony_ci * if a tranport event has occurred leave 12018c2ecf20Sopenharmony_ci * everything but transport events on the queue 12028c2ecf20Sopenharmony_ci */ 12038c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "poll_cmd_q, ignoring\n"); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* 12068c2ecf20Sopenharmony_ci * need to decrement the queue index so we can 12078c2ecf20Sopenharmony_ci * look at the elment again 12088c2ecf20Sopenharmony_ci */ 12098c2ecf20Sopenharmony_ci if (vscsi->cmd_q.index) 12108c2ecf20Sopenharmony_ci vscsi->cmd_q.index -= 1; 12118c2ecf20Sopenharmony_ci else 12128c2ecf20Sopenharmony_ci /* 12138c2ecf20Sopenharmony_ci * index is at 0 it just wrapped. 12148c2ecf20Sopenharmony_ci * have it index last element in q 12158c2ecf20Sopenharmony_ci */ 12168c2ecf20Sopenharmony_ci vscsi->cmd_q.index = vscsi->cmd_q.mask; 12178c2ecf20Sopenharmony_ci break; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci crq->valid = INVALIDATE_CMD_RESP_EL; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index; 12248c2ecf20Sopenharmony_ci valid = crq->valid; 12258c2ecf20Sopenharmony_ci dma_rmb(); 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (!rc) { 12298c2ecf20Sopenharmony_ci if (ack) { 12308c2ecf20Sopenharmony_ci vio_enable_interrupts(vscsi->dma_dev); 12318c2ecf20Sopenharmony_ci ack = false; 12328c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "poll_cmd_q, reenabling interrupts\n"); 12338c2ecf20Sopenharmony_ci } 12348c2ecf20Sopenharmony_ci valid = crq->valid; 12358c2ecf20Sopenharmony_ci dma_rmb(); 12368c2ecf20Sopenharmony_ci if (valid) 12378c2ecf20Sopenharmony_ci goto poll_work; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Leaving poll_cmd_q: rc %ld\n", rc); 12418c2ecf20Sopenharmony_ci} 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci/** 12448c2ecf20Sopenharmony_ci * ibmvscsis_free_cmd_qs() - Free elements in queue 12458c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 12468c2ecf20Sopenharmony_ci * 12478c2ecf20Sopenharmony_ci * Free all of the elements on all queues that are waiting for 12488c2ecf20Sopenharmony_ci * whatever reason. 12498c2ecf20Sopenharmony_ci * 12508c2ecf20Sopenharmony_ci * PRECONDITION: 12518c2ecf20Sopenharmony_ci * Called with interrupt lock held 12528c2ecf20Sopenharmony_ci */ 12538c2ecf20Sopenharmony_cistatic void ibmvscsis_free_cmd_qs(struct scsi_info *vscsi) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd, *nxt; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "free_cmd_qs: waiting_rsp empty %d, timer starter %d\n", 12588c2ecf20Sopenharmony_ci (int)list_empty(&vscsi->waiting_rsp), 12598c2ecf20Sopenharmony_ci vscsi->rsp_q_timer.started); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp, list) { 12628c2ecf20Sopenharmony_ci list_del(&cmd->list); 12638c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, cmd); 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci} 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci/** 12688c2ecf20Sopenharmony_ci * ibmvscsis_get_free_cmd() - Get free command from list 12698c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 12708c2ecf20Sopenharmony_ci * 12718c2ecf20Sopenharmony_ci * Must be called with interrupt lock held. 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_cistatic struct ibmvscsis_cmd *ibmvscsis_get_free_cmd(struct scsi_info *vscsi) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd = NULL; 12768c2ecf20Sopenharmony_ci struct iu_entry *iue; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci iue = srp_iu_get(&vscsi->target); 12798c2ecf20Sopenharmony_ci if (iue) { 12808c2ecf20Sopenharmony_ci cmd = list_first_entry_or_null(&vscsi->free_cmd, 12818c2ecf20Sopenharmony_ci struct ibmvscsis_cmd, list); 12828c2ecf20Sopenharmony_ci if (cmd) { 12838c2ecf20Sopenharmony_ci if (cmd->abort_cmd) 12848c2ecf20Sopenharmony_ci cmd->abort_cmd = NULL; 12858c2ecf20Sopenharmony_ci cmd->flags &= ~(DELAY_SEND); 12868c2ecf20Sopenharmony_ci list_del(&cmd->list); 12878c2ecf20Sopenharmony_ci cmd->iue = iue; 12888c2ecf20Sopenharmony_ci cmd->type = UNSET_TYPE; 12898c2ecf20Sopenharmony_ci memset(&cmd->se_cmd, 0, sizeof(cmd->se_cmd)); 12908c2ecf20Sopenharmony_ci } else { 12918c2ecf20Sopenharmony_ci srp_iu_put(iue); 12928c2ecf20Sopenharmony_ci } 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci return cmd; 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci/** 12998c2ecf20Sopenharmony_ci * ibmvscsis_adapter_idle() - Helper function to handle idle adapter 13008c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 13018c2ecf20Sopenharmony_ci * 13028c2ecf20Sopenharmony_ci * This function is called when the adapter is idle when the driver 13038c2ecf20Sopenharmony_ci * is attempting to clear an error condition. 13048c2ecf20Sopenharmony_ci * The adapter is considered busy if any of its cmd queues 13058c2ecf20Sopenharmony_ci * are non-empty. This function can be invoked 13068c2ecf20Sopenharmony_ci * from the off level disconnect function. 13078c2ecf20Sopenharmony_ci * 13088c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 13098c2ecf20Sopenharmony_ci * Process environment called with interrupt lock held 13108c2ecf20Sopenharmony_ci */ 13118c2ecf20Sopenharmony_cistatic void ibmvscsis_adapter_idle(struct scsi_info *vscsi) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci int free_qs = false; 13148c2ecf20Sopenharmony_ci long rc = 0; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "adapter_idle: flags 0x%x, state 0x%hx\n", 13178c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci /* Only need to free qs if we're disconnecting from client */ 13208c2ecf20Sopenharmony_ci if (vscsi->state != WAIT_CONNECTION || vscsi->flags & TRANS_EVENT) 13218c2ecf20Sopenharmony_ci free_qs = true; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci switch (vscsi->state) { 13248c2ecf20Sopenharmony_ci case UNCONFIGURING: 13258c2ecf20Sopenharmony_ci ibmvscsis_free_command_q(vscsi); 13268c2ecf20Sopenharmony_ci dma_rmb(); 13278c2ecf20Sopenharmony_ci isync(); 13288c2ecf20Sopenharmony_ci if (vscsi->flags & CFG_SLEEPING) { 13298c2ecf20Sopenharmony_ci vscsi->flags &= ~CFG_SLEEPING; 13308c2ecf20Sopenharmony_ci complete(&vscsi->unconfig); 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci break; 13338c2ecf20Sopenharmony_ci case ERR_DISCONNECT_RECONNECT: 13348c2ecf20Sopenharmony_ci ibmvscsis_reset_queue(vscsi); 13358c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "adapter_idle, disc_rec: flags 0x%x\n", 13368c2ecf20Sopenharmony_ci vscsi->flags); 13378c2ecf20Sopenharmony_ci break; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 13408c2ecf20Sopenharmony_ci ibmvscsis_free_command_q(vscsi); 13418c2ecf20Sopenharmony_ci vscsi->flags &= ~(SCHEDULE_DISCONNECT | DISCONNECT_SCHEDULED); 13428c2ecf20Sopenharmony_ci vscsi->flags |= RESPONSE_Q_DOWN; 13438c2ecf20Sopenharmony_ci if (vscsi->tport.enabled) 13448c2ecf20Sopenharmony_ci vscsi->state = ERR_DISCONNECTED; 13458c2ecf20Sopenharmony_ci else 13468c2ecf20Sopenharmony_ci vscsi->state = WAIT_ENABLED; 13478c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "adapter_idle, disc: flags 0x%x, state 0x%hx\n", 13488c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state); 13498c2ecf20Sopenharmony_ci break; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci case WAIT_IDLE: 13528c2ecf20Sopenharmony_ci vscsi->rsp_q_timer.timer_pops = 0; 13538c2ecf20Sopenharmony_ci vscsi->debit = 0; 13548c2ecf20Sopenharmony_ci vscsi->credit = 0; 13558c2ecf20Sopenharmony_ci if (vscsi->flags & PREP_FOR_SUSPEND_PENDING) { 13568c2ecf20Sopenharmony_ci vscsi->state = vscsi->resume_state; 13578c2ecf20Sopenharmony_ci vscsi->resume_state = 0; 13588c2ecf20Sopenharmony_ci rc = ibmvscsis_ready_for_suspend(vscsi, true); 13598c2ecf20Sopenharmony_ci vscsi->flags &= ~DISCONNECT_SCHEDULED; 13608c2ecf20Sopenharmony_ci if (rc) 13618c2ecf20Sopenharmony_ci break; 13628c2ecf20Sopenharmony_ci } else if (vscsi->flags & TRANS_EVENT) { 13638c2ecf20Sopenharmony_ci vscsi->state = WAIT_CONNECTION; 13648c2ecf20Sopenharmony_ci vscsi->flags &= PRESERVE_FLAG_FIELDS; 13658c2ecf20Sopenharmony_ci } else { 13668c2ecf20Sopenharmony_ci vscsi->state = CONNECTED; 13678c2ecf20Sopenharmony_ci vscsi->flags &= ~DISCONNECT_SCHEDULED; 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "adapter_idle, wait: flags 0x%x, state 0x%hx\n", 13718c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state); 13728c2ecf20Sopenharmony_ci ibmvscsis_poll_cmd_q(vscsi); 13738c2ecf20Sopenharmony_ci break; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci case ERR_DISCONNECTED: 13768c2ecf20Sopenharmony_ci vscsi->flags &= ~DISCONNECT_SCHEDULED; 13778c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "adapter_idle, disconnected: flags 0x%x, state 0x%hx\n", 13788c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state); 13798c2ecf20Sopenharmony_ci break; 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci default: 13828c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "adapter_idle: in invalid state %d\n", 13838c2ecf20Sopenharmony_ci vscsi->state); 13848c2ecf20Sopenharmony_ci break; 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci if (free_qs) 13888c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_qs(vscsi); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci /* 13918c2ecf20Sopenharmony_ci * There is a timing window where we could lose a disconnect request. 13928c2ecf20Sopenharmony_ci * The known path to this window occurs during the DISCONNECT_RECONNECT 13938c2ecf20Sopenharmony_ci * case above: reset_queue calls free_command_q, which will release the 13948c2ecf20Sopenharmony_ci * interrupt lock. During that time, a new post_disconnect call can be 13958c2ecf20Sopenharmony_ci * made with a "more severe" state (DISCONNECT or UNCONFIGURING). 13968c2ecf20Sopenharmony_ci * Because the DISCONNECT_SCHEDULED flag is already set, post_disconnect 13978c2ecf20Sopenharmony_ci * will only set the new_state. Now free_command_q reacquires the intr 13988c2ecf20Sopenharmony_ci * lock and clears the DISCONNECT_SCHEDULED flag (using PRESERVE_FLAG_ 13998c2ecf20Sopenharmony_ci * FIELDS), and the disconnect is lost. This is particularly bad when 14008c2ecf20Sopenharmony_ci * the new disconnect was for UNCONFIGURING, since the unconfigure hangs 14018c2ecf20Sopenharmony_ci * forever. 14028c2ecf20Sopenharmony_ci * Fix is that free command queue sets acr state and acr flags if there 14038c2ecf20Sopenharmony_ci * is a change under the lock 14048c2ecf20Sopenharmony_ci * note free command queue writes to this state it clears it 14058c2ecf20Sopenharmony_ci * before releasing the lock, different drivers call the free command 14068c2ecf20Sopenharmony_ci * queue different times so dont initialize above 14078c2ecf20Sopenharmony_ci */ 14088c2ecf20Sopenharmony_ci if (vscsi->phyp_acr_state != 0) { 14098c2ecf20Sopenharmony_ci /* 14108c2ecf20Sopenharmony_ci * set any bits in flags that may have been cleared by 14118c2ecf20Sopenharmony_ci * a call to free command queue in switch statement 14128c2ecf20Sopenharmony_ci * or reset queue 14138c2ecf20Sopenharmony_ci */ 14148c2ecf20Sopenharmony_ci vscsi->flags |= vscsi->phyp_acr_flags; 14158c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, vscsi->phyp_acr_state, 0); 14168c2ecf20Sopenharmony_ci vscsi->phyp_acr_state = 0; 14178c2ecf20Sopenharmony_ci vscsi->phyp_acr_flags = 0; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "adapter_idle: flags 0x%x, state 0x%hx, acr_flags 0x%x, acr_state 0x%hx\n", 14208c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state, vscsi->phyp_acr_flags, 14218c2ecf20Sopenharmony_ci vscsi->phyp_acr_state); 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Leaving adapter_idle: flags 0x%x, state 0x%hx, new_state 0x%x\n", 14258c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state, vscsi->new_state); 14268c2ecf20Sopenharmony_ci} 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci/** 14298c2ecf20Sopenharmony_ci * ibmvscsis_copy_crq_packet() - Copy CRQ Packet 14308c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 14318c2ecf20Sopenharmony_ci * @cmd: Pointer to command element to use to process the request 14328c2ecf20Sopenharmony_ci * @crq: Pointer to CRQ entry containing the request 14338c2ecf20Sopenharmony_ci * 14348c2ecf20Sopenharmony_ci * Copy the srp information unit from the hosted 14358c2ecf20Sopenharmony_ci * partition using remote dma 14368c2ecf20Sopenharmony_ci * 14378c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 14388c2ecf20Sopenharmony_ci * Interrupt, interrupt lock held 14398c2ecf20Sopenharmony_ci */ 14408c2ecf20Sopenharmony_cistatic long ibmvscsis_copy_crq_packet(struct scsi_info *vscsi, 14418c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd, 14428c2ecf20Sopenharmony_ci struct viosrp_crq *crq) 14438c2ecf20Sopenharmony_ci{ 14448c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 14458c2ecf20Sopenharmony_ci long rc = 0; 14468c2ecf20Sopenharmony_ci u16 len; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci len = be16_to_cpu(crq->IU_length); 14498c2ecf20Sopenharmony_ci if ((len > SRP_MAX_IU_LEN) || (len == 0)) { 14508c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "copy_crq: Invalid len %d passed", len); 14518c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 14528c2ecf20Sopenharmony_ci return SRP_VIOLATION; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci rc = h_copy_rdma(len, vscsi->dds.window[REMOTE].liobn, 14568c2ecf20Sopenharmony_ci be64_to_cpu(crq->IU_data_ptr), 14578c2ecf20Sopenharmony_ci vscsi->dds.window[LOCAL].liobn, iue->sbuf->dma); 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci switch (rc) { 14608c2ecf20Sopenharmony_ci case H_SUCCESS: 14618c2ecf20Sopenharmony_ci cmd->init_time = mftb(); 14628c2ecf20Sopenharmony_ci iue->remote_token = crq->IU_data_ptr; 14638c2ecf20Sopenharmony_ci iue->iu_len = len; 14648c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "copy_crq: ioba 0x%llx, init_time 0x%llx\n", 14658c2ecf20Sopenharmony_ci be64_to_cpu(crq->IU_data_ptr), cmd->init_time); 14668c2ecf20Sopenharmony_ci break; 14678c2ecf20Sopenharmony_ci case H_PERMISSION: 14688c2ecf20Sopenharmony_ci if (connection_broken(vscsi)) 14698c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, 14708c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT, 14718c2ecf20Sopenharmony_ci (RESPONSE_Q_DOWN | 14728c2ecf20Sopenharmony_ci CLIENT_FAILED)); 14738c2ecf20Sopenharmony_ci else 14748c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, 14758c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT, 0); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "copy_crq: h_copy_rdma failed, rc %ld\n", 14788c2ecf20Sopenharmony_ci rc); 14798c2ecf20Sopenharmony_ci break; 14808c2ecf20Sopenharmony_ci case H_DEST_PARM: 14818c2ecf20Sopenharmony_ci case H_SOURCE_PARM: 14828c2ecf20Sopenharmony_ci default: 14838c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "copy_crq: h_copy_rdma failed, rc %ld\n", 14848c2ecf20Sopenharmony_ci rc); 14858c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 14868c2ecf20Sopenharmony_ci break; 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci return rc; 14908c2ecf20Sopenharmony_ci} 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci/** 14938c2ecf20Sopenharmony_ci * ibmvscsis_adapter_info - Service an Adapter Info MAnagement Data gram 14948c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 14958c2ecf20Sopenharmony_ci * @iue: Information Unit containing the Adapter Info MAD request 14968c2ecf20Sopenharmony_ci * 14978c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 14988c2ecf20Sopenharmony_ci * Interrupt adapter lock is held 14998c2ecf20Sopenharmony_ci */ 15008c2ecf20Sopenharmony_cistatic long ibmvscsis_adapter_info(struct scsi_info *vscsi, 15018c2ecf20Sopenharmony_ci struct iu_entry *iue) 15028c2ecf20Sopenharmony_ci{ 15038c2ecf20Sopenharmony_ci struct viosrp_adapter_info *mad = &vio_iu(iue)->mad.adapter_info; 15048c2ecf20Sopenharmony_ci struct mad_adapter_info_data *info; 15058c2ecf20Sopenharmony_ci uint flag_bits = 0; 15068c2ecf20Sopenharmony_ci dma_addr_t token; 15078c2ecf20Sopenharmony_ci long rc; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci mad->common.status = cpu_to_be16(VIOSRP_MAD_SUCCESS); 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci if (be16_to_cpu(mad->common.length) > sizeof(*info)) { 15128c2ecf20Sopenharmony_ci mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED); 15138c2ecf20Sopenharmony_ci return 0; 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci info = dma_alloc_coherent(&vscsi->dma_dev->dev, sizeof(*info), &token, 15178c2ecf20Sopenharmony_ci GFP_ATOMIC); 15188c2ecf20Sopenharmony_ci if (!info) { 15198c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "bad dma_alloc_coherent %p\n", 15208c2ecf20Sopenharmony_ci iue->target); 15218c2ecf20Sopenharmony_ci mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED); 15228c2ecf20Sopenharmony_ci return 0; 15238c2ecf20Sopenharmony_ci } 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci /* Get remote info */ 15268c2ecf20Sopenharmony_ci rc = h_copy_rdma(be16_to_cpu(mad->common.length), 15278c2ecf20Sopenharmony_ci vscsi->dds.window[REMOTE].liobn, 15288c2ecf20Sopenharmony_ci be64_to_cpu(mad->buffer), 15298c2ecf20Sopenharmony_ci vscsi->dds.window[LOCAL].liobn, token); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci if (rc != H_SUCCESS) { 15328c2ecf20Sopenharmony_ci if (rc == H_PERMISSION) { 15338c2ecf20Sopenharmony_ci if (connection_broken(vscsi)) 15348c2ecf20Sopenharmony_ci flag_bits = (RESPONSE_Q_DOWN | CLIENT_FAILED); 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci dev_warn(&vscsi->dev, "adapter_info: h_copy_rdma from client failed, rc %ld\n", 15378c2ecf20Sopenharmony_ci rc); 15388c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "adapter_info: ioba 0x%llx, flags 0x%x, flag_bits 0x%x\n", 15398c2ecf20Sopenharmony_ci be64_to_cpu(mad->buffer), vscsi->flags, flag_bits); 15408c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 15418c2ecf20Sopenharmony_ci flag_bits); 15428c2ecf20Sopenharmony_ci goto free_dma; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci /* 15468c2ecf20Sopenharmony_ci * Copy client info, but ignore partition number, which we 15478c2ecf20Sopenharmony_ci * already got from phyp - unless we failed to get it from 15488c2ecf20Sopenharmony_ci * phyp (e.g. if we're running on a p5 system). 15498c2ecf20Sopenharmony_ci */ 15508c2ecf20Sopenharmony_ci if (vscsi->client_data.partition_number == 0) 15518c2ecf20Sopenharmony_ci vscsi->client_data.partition_number = 15528c2ecf20Sopenharmony_ci be32_to_cpu(info->partition_number); 15538c2ecf20Sopenharmony_ci strncpy(vscsi->client_data.srp_version, info->srp_version, 15548c2ecf20Sopenharmony_ci sizeof(vscsi->client_data.srp_version)); 15558c2ecf20Sopenharmony_ci strncpy(vscsi->client_data.partition_name, info->partition_name, 15568c2ecf20Sopenharmony_ci sizeof(vscsi->client_data.partition_name)); 15578c2ecf20Sopenharmony_ci vscsi->client_data.mad_version = be32_to_cpu(info->mad_version); 15588c2ecf20Sopenharmony_ci vscsi->client_data.os_type = be32_to_cpu(info->os_type); 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci /* Copy our info */ 15618c2ecf20Sopenharmony_ci strncpy(info->srp_version, SRP_VERSION, 15628c2ecf20Sopenharmony_ci sizeof(info->srp_version)); 15638c2ecf20Sopenharmony_ci strncpy(info->partition_name, vscsi->dds.partition_name, 15648c2ecf20Sopenharmony_ci sizeof(info->partition_name)); 15658c2ecf20Sopenharmony_ci info->partition_number = cpu_to_be32(vscsi->dds.partition_num); 15668c2ecf20Sopenharmony_ci info->mad_version = cpu_to_be32(MAD_VERSION_1); 15678c2ecf20Sopenharmony_ci info->os_type = cpu_to_be32(LINUX); 15688c2ecf20Sopenharmony_ci memset(&info->port_max_txu[0], 0, sizeof(info->port_max_txu)); 15698c2ecf20Sopenharmony_ci info->port_max_txu[0] = cpu_to_be32(MAX_TXU); 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci dma_wmb(); 15728c2ecf20Sopenharmony_ci rc = h_copy_rdma(sizeof(*info), vscsi->dds.window[LOCAL].liobn, 15738c2ecf20Sopenharmony_ci token, vscsi->dds.window[REMOTE].liobn, 15748c2ecf20Sopenharmony_ci be64_to_cpu(mad->buffer)); 15758c2ecf20Sopenharmony_ci switch (rc) { 15768c2ecf20Sopenharmony_ci case H_SUCCESS: 15778c2ecf20Sopenharmony_ci break; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci case H_SOURCE_PARM: 15808c2ecf20Sopenharmony_ci case H_DEST_PARM: 15818c2ecf20Sopenharmony_ci case H_PERMISSION: 15828c2ecf20Sopenharmony_ci if (connection_broken(vscsi)) 15838c2ecf20Sopenharmony_ci flag_bits = (RESPONSE_Q_DOWN | CLIENT_FAILED); 15848c2ecf20Sopenharmony_ci fallthrough; 15858c2ecf20Sopenharmony_ci default: 15868c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "adapter_info: h_copy_rdma to client failed, rc %ld\n", 15878c2ecf20Sopenharmony_ci rc); 15888c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, 15898c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT, 15908c2ecf20Sopenharmony_ci flag_bits); 15918c2ecf20Sopenharmony_ci break; 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_cifree_dma: 15958c2ecf20Sopenharmony_ci dma_free_coherent(&vscsi->dma_dev->dev, sizeof(*info), info, token); 15968c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Leaving adapter_info, rc %ld\n", rc); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci return rc; 15998c2ecf20Sopenharmony_ci} 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci/** 16028c2ecf20Sopenharmony_ci * ibmvscsis_cap_mad() - Service a Capabilities MAnagement Data gram 16038c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 16048c2ecf20Sopenharmony_ci * @iue: Information Unit containing the Capabilities MAD request 16058c2ecf20Sopenharmony_ci * 16068c2ecf20Sopenharmony_ci * NOTE: if you return an error from this routine you must be 16078c2ecf20Sopenharmony_ci * disconnecting or you will cause a hang 16088c2ecf20Sopenharmony_ci * 16098c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 16108c2ecf20Sopenharmony_ci * Interrupt called with adapter lock held 16118c2ecf20Sopenharmony_ci */ 16128c2ecf20Sopenharmony_cistatic int ibmvscsis_cap_mad(struct scsi_info *vscsi, struct iu_entry *iue) 16138c2ecf20Sopenharmony_ci{ 16148c2ecf20Sopenharmony_ci struct viosrp_capabilities *mad = &vio_iu(iue)->mad.capabilities; 16158c2ecf20Sopenharmony_ci struct capabilities *cap; 16168c2ecf20Sopenharmony_ci struct mad_capability_common *common; 16178c2ecf20Sopenharmony_ci dma_addr_t token; 16188c2ecf20Sopenharmony_ci u16 olen, len, status, min_len, cap_len; 16198c2ecf20Sopenharmony_ci u32 flag; 16208c2ecf20Sopenharmony_ci uint flag_bits = 0; 16218c2ecf20Sopenharmony_ci long rc = 0; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci olen = be16_to_cpu(mad->common.length); 16248c2ecf20Sopenharmony_ci /* 16258c2ecf20Sopenharmony_ci * struct capabilities hardcodes a couple capabilities after the 16268c2ecf20Sopenharmony_ci * header, but the capabilities can actually be in any order. 16278c2ecf20Sopenharmony_ci */ 16288c2ecf20Sopenharmony_ci min_len = offsetof(struct capabilities, migration); 16298c2ecf20Sopenharmony_ci if ((olen < min_len) || (olen > PAGE_SIZE)) { 16308c2ecf20Sopenharmony_ci dev_warn(&vscsi->dev, "cap_mad: invalid len %d\n", olen); 16318c2ecf20Sopenharmony_ci mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED); 16328c2ecf20Sopenharmony_ci return 0; 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci cap = dma_alloc_coherent(&vscsi->dma_dev->dev, olen, &token, 16368c2ecf20Sopenharmony_ci GFP_ATOMIC); 16378c2ecf20Sopenharmony_ci if (!cap) { 16388c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "bad dma_alloc_coherent %p\n", 16398c2ecf20Sopenharmony_ci iue->target); 16408c2ecf20Sopenharmony_ci mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED); 16418c2ecf20Sopenharmony_ci return 0; 16428c2ecf20Sopenharmony_ci } 16438c2ecf20Sopenharmony_ci rc = h_copy_rdma(olen, vscsi->dds.window[REMOTE].liobn, 16448c2ecf20Sopenharmony_ci be64_to_cpu(mad->buffer), 16458c2ecf20Sopenharmony_ci vscsi->dds.window[LOCAL].liobn, token); 16468c2ecf20Sopenharmony_ci if (rc == H_SUCCESS) { 16478c2ecf20Sopenharmony_ci strncpy(cap->name, dev_name(&vscsi->dma_dev->dev), 16488c2ecf20Sopenharmony_ci SRP_MAX_LOC_LEN); 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci len = olen - min_len; 16518c2ecf20Sopenharmony_ci status = VIOSRP_MAD_SUCCESS; 16528c2ecf20Sopenharmony_ci common = (struct mad_capability_common *)&cap->migration; 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci while ((len > 0) && (status == VIOSRP_MAD_SUCCESS) && !rc) { 16558c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "cap_mad: len left %hd, cap type %d, cap len %hd\n", 16568c2ecf20Sopenharmony_ci len, be32_to_cpu(common->cap_type), 16578c2ecf20Sopenharmony_ci be16_to_cpu(common->length)); 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci cap_len = be16_to_cpu(common->length); 16608c2ecf20Sopenharmony_ci if (cap_len > len) { 16618c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "cap_mad: cap len mismatch with total len\n"); 16628c2ecf20Sopenharmony_ci status = VIOSRP_MAD_FAILED; 16638c2ecf20Sopenharmony_ci break; 16648c2ecf20Sopenharmony_ci } 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci if (cap_len == 0) { 16678c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "cap_mad: cap len is 0\n"); 16688c2ecf20Sopenharmony_ci status = VIOSRP_MAD_FAILED; 16698c2ecf20Sopenharmony_ci break; 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci switch (common->cap_type) { 16738c2ecf20Sopenharmony_ci default: 16748c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "cap_mad: unsupported capability\n"); 16758c2ecf20Sopenharmony_ci common->server_support = 0; 16768c2ecf20Sopenharmony_ci flag = cpu_to_be32((u32)CAP_LIST_SUPPORTED); 16778c2ecf20Sopenharmony_ci cap->flags &= ~flag; 16788c2ecf20Sopenharmony_ci break; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci len = len - cap_len; 16828c2ecf20Sopenharmony_ci common = (struct mad_capability_common *) 16838c2ecf20Sopenharmony_ci ((char *)common + cap_len); 16848c2ecf20Sopenharmony_ci } 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci mad->common.status = cpu_to_be16(status); 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci dma_wmb(); 16898c2ecf20Sopenharmony_ci rc = h_copy_rdma(olen, vscsi->dds.window[LOCAL].liobn, token, 16908c2ecf20Sopenharmony_ci vscsi->dds.window[REMOTE].liobn, 16918c2ecf20Sopenharmony_ci be64_to_cpu(mad->buffer)); 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci if (rc != H_SUCCESS) { 16948c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "cap_mad: failed to copy to client, rc %ld\n", 16958c2ecf20Sopenharmony_ci rc); 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci if (rc == H_PERMISSION) { 16988c2ecf20Sopenharmony_ci if (connection_broken(vscsi)) 16998c2ecf20Sopenharmony_ci flag_bits = (RESPONSE_Q_DOWN | 17008c2ecf20Sopenharmony_ci CLIENT_FAILED); 17018c2ecf20Sopenharmony_ci } 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci dev_warn(&vscsi->dev, "cap_mad: error copying data to client, rc %ld\n", 17048c2ecf20Sopenharmony_ci rc); 17058c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, 17068c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT, 17078c2ecf20Sopenharmony_ci flag_bits); 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci dma_free_coherent(&vscsi->dma_dev->dev, olen, cap, token); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Leaving cap_mad, rc %ld, client_cap 0x%x\n", 17148c2ecf20Sopenharmony_ci rc, vscsi->client_cap); 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci return rc; 17178c2ecf20Sopenharmony_ci} 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci/** 17208c2ecf20Sopenharmony_ci * ibmvscsis_process_mad() - Service a MAnagement Data gram 17218c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 17228c2ecf20Sopenharmony_ci * @iue: Information Unit containing the MAD request 17238c2ecf20Sopenharmony_ci * 17248c2ecf20Sopenharmony_ci * Must be called with interrupt lock held. 17258c2ecf20Sopenharmony_ci */ 17268c2ecf20Sopenharmony_cistatic long ibmvscsis_process_mad(struct scsi_info *vscsi, struct iu_entry *iue) 17278c2ecf20Sopenharmony_ci{ 17288c2ecf20Sopenharmony_ci struct mad_common *mad = (struct mad_common *)&vio_iu(iue)->mad; 17298c2ecf20Sopenharmony_ci struct viosrp_empty_iu *empty; 17308c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci switch (be32_to_cpu(mad->type)) { 17338c2ecf20Sopenharmony_ci case VIOSRP_EMPTY_IU_TYPE: 17348c2ecf20Sopenharmony_ci empty = &vio_iu(iue)->mad.empty_iu; 17358c2ecf20Sopenharmony_ci vscsi->empty_iu_id = be64_to_cpu(empty->buffer); 17368c2ecf20Sopenharmony_ci vscsi->empty_iu_tag = be64_to_cpu(empty->common.tag); 17378c2ecf20Sopenharmony_ci mad->status = cpu_to_be16(VIOSRP_MAD_SUCCESS); 17388c2ecf20Sopenharmony_ci break; 17398c2ecf20Sopenharmony_ci case VIOSRP_ADAPTER_INFO_TYPE: 17408c2ecf20Sopenharmony_ci rc = ibmvscsis_adapter_info(vscsi, iue); 17418c2ecf20Sopenharmony_ci break; 17428c2ecf20Sopenharmony_ci case VIOSRP_CAPABILITIES_TYPE: 17438c2ecf20Sopenharmony_ci rc = ibmvscsis_cap_mad(vscsi, iue); 17448c2ecf20Sopenharmony_ci break; 17458c2ecf20Sopenharmony_ci case VIOSRP_ENABLE_FAST_FAIL: 17468c2ecf20Sopenharmony_ci if (vscsi->state == CONNECTED) { 17478c2ecf20Sopenharmony_ci vscsi->fast_fail = true; 17488c2ecf20Sopenharmony_ci mad->status = cpu_to_be16(VIOSRP_MAD_SUCCESS); 17498c2ecf20Sopenharmony_ci } else { 17508c2ecf20Sopenharmony_ci dev_warn(&vscsi->dev, "fast fail mad sent after login\n"); 17518c2ecf20Sopenharmony_ci mad->status = cpu_to_be16(VIOSRP_MAD_FAILED); 17528c2ecf20Sopenharmony_ci } 17538c2ecf20Sopenharmony_ci break; 17548c2ecf20Sopenharmony_ci default: 17558c2ecf20Sopenharmony_ci mad->status = cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED); 17568c2ecf20Sopenharmony_ci break; 17578c2ecf20Sopenharmony_ci } 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci return rc; 17608c2ecf20Sopenharmony_ci} 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci/** 17638c2ecf20Sopenharmony_ci * srp_snd_msg_failed() - Handle an error when sending a response 17648c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 17658c2ecf20Sopenharmony_ci * @rc: The return code from the h_send_crq command 17668c2ecf20Sopenharmony_ci * 17678c2ecf20Sopenharmony_ci * Must be called with interrupt lock held. 17688c2ecf20Sopenharmony_ci */ 17698c2ecf20Sopenharmony_cistatic void srp_snd_msg_failed(struct scsi_info *vscsi, long rc) 17708c2ecf20Sopenharmony_ci{ 17718c2ecf20Sopenharmony_ci ktime_t kt; 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci if (rc != H_DROPPED) { 17748c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_qs(vscsi); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci if (rc == H_CLOSED) 17778c2ecf20Sopenharmony_ci vscsi->flags |= CLIENT_FAILED; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci /* don't flag the same problem multiple times */ 17808c2ecf20Sopenharmony_ci if (!(vscsi->flags & RESPONSE_Q_DOWN)) { 17818c2ecf20Sopenharmony_ci vscsi->flags |= RESPONSE_Q_DOWN; 17828c2ecf20Sopenharmony_ci if (!(vscsi->state & (ERR_DISCONNECT | 17838c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT | 17848c2ecf20Sopenharmony_ci ERR_DISCONNECTED | UNDEFINED))) { 17858c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "snd_msg_failed: setting RESPONSE_Q_DOWN, state 0x%hx, flags 0x%x, rc %ld\n", 17868c2ecf20Sopenharmony_ci vscsi->state, vscsi->flags, rc); 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, 17898c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT, 0); 17908c2ecf20Sopenharmony_ci } 17918c2ecf20Sopenharmony_ci return; 17928c2ecf20Sopenharmony_ci } 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci /* 17958c2ecf20Sopenharmony_ci * The response queue is full. 17968c2ecf20Sopenharmony_ci * If the server is processing SRP requests, i.e. 17978c2ecf20Sopenharmony_ci * the client has successfully done an 17988c2ecf20Sopenharmony_ci * SRP_LOGIN, then it will wait forever for room in 17998c2ecf20Sopenharmony_ci * the queue. However if the system admin 18008c2ecf20Sopenharmony_ci * is attempting to unconfigure the server then one 18018c2ecf20Sopenharmony_ci * or more children will be in a state where 18028c2ecf20Sopenharmony_ci * they are being removed. So if there is even one 18038c2ecf20Sopenharmony_ci * child being removed then the driver assumes 18048c2ecf20Sopenharmony_ci * the system admin is attempting to break the 18058c2ecf20Sopenharmony_ci * connection with the client and MAX_TIMER_POPS 18068c2ecf20Sopenharmony_ci * is honored. 18078c2ecf20Sopenharmony_ci */ 18088c2ecf20Sopenharmony_ci if ((vscsi->rsp_q_timer.timer_pops < MAX_TIMER_POPS) || 18098c2ecf20Sopenharmony_ci (vscsi->state == SRP_PROCESSING)) { 18108c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "snd_msg_failed: response queue full, flags 0x%x, timer started %d, pops %d\n", 18118c2ecf20Sopenharmony_ci vscsi->flags, (int)vscsi->rsp_q_timer.started, 18128c2ecf20Sopenharmony_ci vscsi->rsp_q_timer.timer_pops); 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci /* 18158c2ecf20Sopenharmony_ci * Check if the timer is running; if it 18168c2ecf20Sopenharmony_ci * is not then start it up. 18178c2ecf20Sopenharmony_ci */ 18188c2ecf20Sopenharmony_ci if (!vscsi->rsp_q_timer.started) { 18198c2ecf20Sopenharmony_ci if (vscsi->rsp_q_timer.timer_pops < 18208c2ecf20Sopenharmony_ci MAX_TIMER_POPS) { 18218c2ecf20Sopenharmony_ci kt = WAIT_NANO_SECONDS; 18228c2ecf20Sopenharmony_ci } else { 18238c2ecf20Sopenharmony_ci /* 18248c2ecf20Sopenharmony_ci * slide the timeslice if the maximum 18258c2ecf20Sopenharmony_ci * timer pops have already happened 18268c2ecf20Sopenharmony_ci */ 18278c2ecf20Sopenharmony_ci kt = ktime_set(WAIT_SECONDS, 0); 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci vscsi->rsp_q_timer.started = true; 18318c2ecf20Sopenharmony_ci hrtimer_start(&vscsi->rsp_q_timer.timer, kt, 18328c2ecf20Sopenharmony_ci HRTIMER_MODE_REL); 18338c2ecf20Sopenharmony_ci } 18348c2ecf20Sopenharmony_ci } else { 18358c2ecf20Sopenharmony_ci /* 18368c2ecf20Sopenharmony_ci * TBD: Do we need to worry about this? Need to get 18378c2ecf20Sopenharmony_ci * remove working. 18388c2ecf20Sopenharmony_ci */ 18398c2ecf20Sopenharmony_ci /* 18408c2ecf20Sopenharmony_ci * waited a long time and it appears the system admin 18418c2ecf20Sopenharmony_ci * is bring this driver down 18428c2ecf20Sopenharmony_ci */ 18438c2ecf20Sopenharmony_ci vscsi->flags |= RESPONSE_Q_DOWN; 18448c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_qs(vscsi); 18458c2ecf20Sopenharmony_ci /* 18468c2ecf20Sopenharmony_ci * if the driver is already attempting to disconnect 18478c2ecf20Sopenharmony_ci * from the client and has already logged an error 18488c2ecf20Sopenharmony_ci * trace this event but don't put it in the error log 18498c2ecf20Sopenharmony_ci */ 18508c2ecf20Sopenharmony_ci if (!(vscsi->state & (ERR_DISCONNECT | 18518c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT | 18528c2ecf20Sopenharmony_ci ERR_DISCONNECTED | UNDEFINED))) { 18538c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "client crq full too long\n"); 18548c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, 18558c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT, 18568c2ecf20Sopenharmony_ci 0); 18578c2ecf20Sopenharmony_ci } 18588c2ecf20Sopenharmony_ci } 18598c2ecf20Sopenharmony_ci} 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci/** 18628c2ecf20Sopenharmony_ci * ibmvscsis_send_messages() - Send a Response 18638c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 18648c2ecf20Sopenharmony_ci * 18658c2ecf20Sopenharmony_ci * Send a response, first checking the waiting queue. Responses are 18668c2ecf20Sopenharmony_ci * sent in order they are received. If the response cannot be sent, 18678c2ecf20Sopenharmony_ci * because the client queue is full, it stays on the waiting queue. 18688c2ecf20Sopenharmony_ci * 18698c2ecf20Sopenharmony_ci * PRECONDITION: 18708c2ecf20Sopenharmony_ci * Called with interrupt lock held 18718c2ecf20Sopenharmony_ci */ 18728c2ecf20Sopenharmony_cistatic void ibmvscsis_send_messages(struct scsi_info *vscsi) 18738c2ecf20Sopenharmony_ci{ 18748c2ecf20Sopenharmony_ci u64 msg_hi = 0; 18758c2ecf20Sopenharmony_ci /* note do not attempt to access the IU_data_ptr with this pointer 18768c2ecf20Sopenharmony_ci * it is not valid 18778c2ecf20Sopenharmony_ci */ 18788c2ecf20Sopenharmony_ci struct viosrp_crq *crq = (struct viosrp_crq *)&msg_hi; 18798c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd, *nxt; 18808c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 18818c2ecf20Sopenharmony_ci bool retry = false; 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci if (!(vscsi->flags & RESPONSE_Q_DOWN)) { 18848c2ecf20Sopenharmony_ci do { 18858c2ecf20Sopenharmony_ci retry = false; 18868c2ecf20Sopenharmony_ci list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp, 18878c2ecf20Sopenharmony_ci list) { 18888c2ecf20Sopenharmony_ci /* 18898c2ecf20Sopenharmony_ci * Check to make sure abort cmd gets processed 18908c2ecf20Sopenharmony_ci * prior to the abort tmr cmd 18918c2ecf20Sopenharmony_ci */ 18928c2ecf20Sopenharmony_ci if (cmd->flags & DELAY_SEND) 18938c2ecf20Sopenharmony_ci continue; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci if (cmd->abort_cmd) { 18968c2ecf20Sopenharmony_ci retry = true; 18978c2ecf20Sopenharmony_ci cmd->abort_cmd->flags &= ~(DELAY_SEND); 18988c2ecf20Sopenharmony_ci cmd->abort_cmd = NULL; 18998c2ecf20Sopenharmony_ci } 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci /* 19028c2ecf20Sopenharmony_ci * If CMD_T_ABORTED w/o CMD_T_TAS scenarios and 19038c2ecf20Sopenharmony_ci * the case where LIO issued a 19048c2ecf20Sopenharmony_ci * ABORT_TASK: Sending TMR_TASK_DOES_NOT_EXIST 19058c2ecf20Sopenharmony_ci * case then we dont send a response, since it 19068c2ecf20Sopenharmony_ci * was already done. 19078c2ecf20Sopenharmony_ci */ 19088c2ecf20Sopenharmony_ci if (cmd->se_cmd.transport_state & CMD_T_ABORTED && 19098c2ecf20Sopenharmony_ci !(cmd->se_cmd.transport_state & CMD_T_TAS)) { 19108c2ecf20Sopenharmony_ci list_del(&cmd->list); 19118c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, 19128c2ecf20Sopenharmony_ci cmd); 19138c2ecf20Sopenharmony_ci /* 19148c2ecf20Sopenharmony_ci * With a successfully aborted op 19158c2ecf20Sopenharmony_ci * through LIO we want to increment the 19168c2ecf20Sopenharmony_ci * the vscsi credit so that when we dont 19178c2ecf20Sopenharmony_ci * send a rsp to the original scsi abort 19188c2ecf20Sopenharmony_ci * op (h_send_crq), but the tm rsp to 19198c2ecf20Sopenharmony_ci * the abort is sent, the credit is 19208c2ecf20Sopenharmony_ci * correctly sent with the abort tm rsp. 19218c2ecf20Sopenharmony_ci * We would need 1 for the abort tm rsp 19228c2ecf20Sopenharmony_ci * and 1 credit for the aborted scsi op. 19238c2ecf20Sopenharmony_ci * Thus we need to increment here. 19248c2ecf20Sopenharmony_ci * Also we want to increment the credit 19258c2ecf20Sopenharmony_ci * here because we want to make sure 19268c2ecf20Sopenharmony_ci * cmd is actually released first 19278c2ecf20Sopenharmony_ci * otherwise the client will think it 19288c2ecf20Sopenharmony_ci * it can send a new cmd, and we could 19298c2ecf20Sopenharmony_ci * find ourselves short of cmd elements. 19308c2ecf20Sopenharmony_ci */ 19318c2ecf20Sopenharmony_ci vscsi->credit += 1; 19328c2ecf20Sopenharmony_ci } else { 19338c2ecf20Sopenharmony_ci crq->valid = VALID_CMD_RESP_EL; 19348c2ecf20Sopenharmony_ci crq->format = cmd->rsp.format; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci if (cmd->flags & CMD_FAST_FAIL) 19378c2ecf20Sopenharmony_ci crq->status = VIOSRP_ADAPTER_FAIL; 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci crq->IU_length = cpu_to_be16(cmd->rsp.len); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci rc = h_send_crq(vscsi->dma_dev->unit_address, 19428c2ecf20Sopenharmony_ci be64_to_cpu(msg_hi), 19438c2ecf20Sopenharmony_ci be64_to_cpu(cmd->rsp.tag)); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "send_messages: cmd %p, tag 0x%llx, rc %ld\n", 19468c2ecf20Sopenharmony_ci cmd, be64_to_cpu(cmd->rsp.tag), 19478c2ecf20Sopenharmony_ci rc); 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci /* if all ok free up the command 19508c2ecf20Sopenharmony_ci * element resources 19518c2ecf20Sopenharmony_ci */ 19528c2ecf20Sopenharmony_ci if (rc == H_SUCCESS) { 19538c2ecf20Sopenharmony_ci /* some movement has occurred */ 19548c2ecf20Sopenharmony_ci vscsi->rsp_q_timer.timer_pops = 0; 19558c2ecf20Sopenharmony_ci list_del(&cmd->list); 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, 19588c2ecf20Sopenharmony_ci cmd); 19598c2ecf20Sopenharmony_ci } else { 19608c2ecf20Sopenharmony_ci srp_snd_msg_failed(vscsi, rc); 19618c2ecf20Sopenharmony_ci break; 19628c2ecf20Sopenharmony_ci } 19638c2ecf20Sopenharmony_ci } 19648c2ecf20Sopenharmony_ci } 19658c2ecf20Sopenharmony_ci } while (retry); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci if (!rc) { 19688c2ecf20Sopenharmony_ci /* 19698c2ecf20Sopenharmony_ci * The timer could pop with the queue empty. If 19708c2ecf20Sopenharmony_ci * this happens, rc will always indicate a 19718c2ecf20Sopenharmony_ci * success; clear the pop count. 19728c2ecf20Sopenharmony_ci */ 19738c2ecf20Sopenharmony_ci vscsi->rsp_q_timer.timer_pops = 0; 19748c2ecf20Sopenharmony_ci } 19758c2ecf20Sopenharmony_ci } else { 19768c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_qs(vscsi); 19778c2ecf20Sopenharmony_ci } 19788c2ecf20Sopenharmony_ci} 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci/* Called with intr lock held */ 19818c2ecf20Sopenharmony_cistatic void ibmvscsis_send_mad_resp(struct scsi_info *vscsi, 19828c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd, 19838c2ecf20Sopenharmony_ci struct viosrp_crq *crq) 19848c2ecf20Sopenharmony_ci{ 19858c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 19868c2ecf20Sopenharmony_ci struct mad_common *mad = (struct mad_common *)&vio_iu(iue)->mad; 19878c2ecf20Sopenharmony_ci uint flag_bits = 0; 19888c2ecf20Sopenharmony_ci long rc; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci dma_wmb(); 19918c2ecf20Sopenharmony_ci rc = h_copy_rdma(sizeof(struct mad_common), 19928c2ecf20Sopenharmony_ci vscsi->dds.window[LOCAL].liobn, iue->sbuf->dma, 19938c2ecf20Sopenharmony_ci vscsi->dds.window[REMOTE].liobn, 19948c2ecf20Sopenharmony_ci be64_to_cpu(crq->IU_data_ptr)); 19958c2ecf20Sopenharmony_ci if (!rc) { 19968c2ecf20Sopenharmony_ci cmd->rsp.format = VIOSRP_MAD_FORMAT; 19978c2ecf20Sopenharmony_ci cmd->rsp.len = sizeof(struct mad_common); 19988c2ecf20Sopenharmony_ci cmd->rsp.tag = mad->tag; 19998c2ecf20Sopenharmony_ci list_add_tail(&cmd->list, &vscsi->waiting_rsp); 20008c2ecf20Sopenharmony_ci ibmvscsis_send_messages(vscsi); 20018c2ecf20Sopenharmony_ci } else { 20028c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Error sending mad response, rc %ld\n", 20038c2ecf20Sopenharmony_ci rc); 20048c2ecf20Sopenharmony_ci if (rc == H_PERMISSION) { 20058c2ecf20Sopenharmony_ci if (connection_broken(vscsi)) 20068c2ecf20Sopenharmony_ci flag_bits = (RESPONSE_Q_DOWN | CLIENT_FAILED); 20078c2ecf20Sopenharmony_ci } 20088c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "mad: failed to copy to client, rc %ld\n", 20098c2ecf20Sopenharmony_ci rc); 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, cmd); 20128c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 20138c2ecf20Sopenharmony_ci flag_bits); 20148c2ecf20Sopenharmony_ci } 20158c2ecf20Sopenharmony_ci} 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci/** 20188c2ecf20Sopenharmony_ci * ibmvscsis_mad() - Service a MAnagement Data gram. 20198c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 20208c2ecf20Sopenharmony_ci * @crq: Pointer to the CRQ entry containing the MAD request 20218c2ecf20Sopenharmony_ci * 20228c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 20238c2ecf20Sopenharmony_ci * Interrupt, called with adapter lock held 20248c2ecf20Sopenharmony_ci */ 20258c2ecf20Sopenharmony_cistatic long ibmvscsis_mad(struct scsi_info *vscsi, struct viosrp_crq *crq) 20268c2ecf20Sopenharmony_ci{ 20278c2ecf20Sopenharmony_ci struct iu_entry *iue; 20288c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd; 20298c2ecf20Sopenharmony_ci struct mad_common *mad; 20308c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci switch (vscsi->state) { 20338c2ecf20Sopenharmony_ci /* 20348c2ecf20Sopenharmony_ci * We have not exchanged Init Msgs yet, so this MAD was sent 20358c2ecf20Sopenharmony_ci * before the last Transport Event; client will not be 20368c2ecf20Sopenharmony_ci * expecting a response. 20378c2ecf20Sopenharmony_ci */ 20388c2ecf20Sopenharmony_ci case WAIT_CONNECTION: 20398c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "mad: in Wait Connection state, ignoring MAD, flags %d\n", 20408c2ecf20Sopenharmony_ci vscsi->flags); 20418c2ecf20Sopenharmony_ci return ADAPT_SUCCESS; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci case SRP_PROCESSING: 20448c2ecf20Sopenharmony_ci case CONNECTED: 20458c2ecf20Sopenharmony_ci break; 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci /* 20488c2ecf20Sopenharmony_ci * We should never get here while we're in these states. 20498c2ecf20Sopenharmony_ci * Just log an error and get out. 20508c2ecf20Sopenharmony_ci */ 20518c2ecf20Sopenharmony_ci case UNCONFIGURING: 20528c2ecf20Sopenharmony_ci case WAIT_IDLE: 20538c2ecf20Sopenharmony_ci case ERR_DISCONNECT: 20548c2ecf20Sopenharmony_ci case ERR_DISCONNECT_RECONNECT: 20558c2ecf20Sopenharmony_ci default: 20568c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "mad: invalid adapter state %d for mad\n", 20578c2ecf20Sopenharmony_ci vscsi->state); 20588c2ecf20Sopenharmony_ci return ADAPT_SUCCESS; 20598c2ecf20Sopenharmony_ci } 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci cmd = ibmvscsis_get_free_cmd(vscsi); 20628c2ecf20Sopenharmony_ci if (!cmd) { 20638c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "mad: failed to get cmd, debit %d\n", 20648c2ecf20Sopenharmony_ci vscsi->debit); 20658c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 20668c2ecf20Sopenharmony_ci return ERROR; 20678c2ecf20Sopenharmony_ci } 20688c2ecf20Sopenharmony_ci iue = cmd->iue; 20698c2ecf20Sopenharmony_ci cmd->type = ADAPTER_MAD; 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci rc = ibmvscsis_copy_crq_packet(vscsi, cmd, crq); 20728c2ecf20Sopenharmony_ci if (!rc) { 20738c2ecf20Sopenharmony_ci mad = (struct mad_common *)&vio_iu(iue)->mad; 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "mad: type %d\n", be32_to_cpu(mad->type)); 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci rc = ibmvscsis_process_mad(vscsi, iue); 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "mad: status %hd, rc %ld\n", 20808c2ecf20Sopenharmony_ci be16_to_cpu(mad->status), rc); 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci if (!rc) 20838c2ecf20Sopenharmony_ci ibmvscsis_send_mad_resp(vscsi, cmd, crq); 20848c2ecf20Sopenharmony_ci } else { 20858c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, cmd); 20868c2ecf20Sopenharmony_ci } 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Leaving mad, rc %ld\n", rc); 20898c2ecf20Sopenharmony_ci return rc; 20908c2ecf20Sopenharmony_ci} 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci/** 20938c2ecf20Sopenharmony_ci * ibmvscsis_login_rsp() - Create/copy a login response notice to the client 20948c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 20958c2ecf20Sopenharmony_ci * @cmd: Pointer to the command for the SRP Login request 20968c2ecf20Sopenharmony_ci * 20978c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 20988c2ecf20Sopenharmony_ci * Interrupt, interrupt lock held 20998c2ecf20Sopenharmony_ci */ 21008c2ecf20Sopenharmony_cistatic long ibmvscsis_login_rsp(struct scsi_info *vscsi, 21018c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd) 21028c2ecf20Sopenharmony_ci{ 21038c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 21048c2ecf20Sopenharmony_ci struct srp_login_rsp *rsp = &vio_iu(iue)->srp.login_rsp; 21058c2ecf20Sopenharmony_ci struct format_code *fmt; 21068c2ecf20Sopenharmony_ci uint flag_bits = 0; 21078c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_ci memset(rsp, 0, sizeof(struct srp_login_rsp)); 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci rsp->opcode = SRP_LOGIN_RSP; 21128c2ecf20Sopenharmony_ci rsp->req_lim_delta = cpu_to_be32(vscsi->request_limit); 21138c2ecf20Sopenharmony_ci rsp->tag = cmd->rsp.tag; 21148c2ecf20Sopenharmony_ci rsp->max_it_iu_len = cpu_to_be32(SRP_MAX_IU_LEN); 21158c2ecf20Sopenharmony_ci rsp->max_ti_iu_len = cpu_to_be32(SRP_MAX_IU_LEN); 21168c2ecf20Sopenharmony_ci fmt = (struct format_code *)&rsp->buf_fmt; 21178c2ecf20Sopenharmony_ci fmt->buffers = SUPPORTED_FORMATS; 21188c2ecf20Sopenharmony_ci vscsi->credit = 0; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci cmd->rsp.len = sizeof(struct srp_login_rsp); 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci dma_wmb(); 21238c2ecf20Sopenharmony_ci rc = h_copy_rdma(cmd->rsp.len, vscsi->dds.window[LOCAL].liobn, 21248c2ecf20Sopenharmony_ci iue->sbuf->dma, vscsi->dds.window[REMOTE].liobn, 21258c2ecf20Sopenharmony_ci be64_to_cpu(iue->remote_token)); 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci switch (rc) { 21288c2ecf20Sopenharmony_ci case H_SUCCESS: 21298c2ecf20Sopenharmony_ci break; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci case H_PERMISSION: 21328c2ecf20Sopenharmony_ci if (connection_broken(vscsi)) 21338c2ecf20Sopenharmony_ci flag_bits = RESPONSE_Q_DOWN | CLIENT_FAILED; 21348c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "login_rsp: error copying to client, rc %ld\n", 21358c2ecf20Sopenharmony_ci rc); 21368c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 21378c2ecf20Sopenharmony_ci flag_bits); 21388c2ecf20Sopenharmony_ci break; 21398c2ecf20Sopenharmony_ci case H_SOURCE_PARM: 21408c2ecf20Sopenharmony_ci case H_DEST_PARM: 21418c2ecf20Sopenharmony_ci default: 21428c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "login_rsp: error copying to client, rc %ld\n", 21438c2ecf20Sopenharmony_ci rc); 21448c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 21458c2ecf20Sopenharmony_ci break; 21468c2ecf20Sopenharmony_ci } 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci return rc; 21498c2ecf20Sopenharmony_ci} 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci/** 21528c2ecf20Sopenharmony_ci * ibmvscsis_srp_login_rej() - Create/copy a login rejection notice to client 21538c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 21548c2ecf20Sopenharmony_ci * @cmd: Pointer to the command for the SRP Login request 21558c2ecf20Sopenharmony_ci * @reason: The reason the SRP Login is being rejected, per SRP protocol 21568c2ecf20Sopenharmony_ci * 21578c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 21588c2ecf20Sopenharmony_ci * Interrupt, interrupt lock held 21598c2ecf20Sopenharmony_ci */ 21608c2ecf20Sopenharmony_cistatic long ibmvscsis_srp_login_rej(struct scsi_info *vscsi, 21618c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd, u32 reason) 21628c2ecf20Sopenharmony_ci{ 21638c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 21648c2ecf20Sopenharmony_ci struct srp_login_rej *rej = &vio_iu(iue)->srp.login_rej; 21658c2ecf20Sopenharmony_ci struct format_code *fmt; 21668c2ecf20Sopenharmony_ci uint flag_bits = 0; 21678c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci memset(rej, 0, sizeof(*rej)); 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci rej->opcode = SRP_LOGIN_REJ; 21728c2ecf20Sopenharmony_ci rej->reason = cpu_to_be32(reason); 21738c2ecf20Sopenharmony_ci rej->tag = cmd->rsp.tag; 21748c2ecf20Sopenharmony_ci fmt = (struct format_code *)&rej->buf_fmt; 21758c2ecf20Sopenharmony_ci fmt->buffers = SUPPORTED_FORMATS; 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci cmd->rsp.len = sizeof(*rej); 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci dma_wmb(); 21808c2ecf20Sopenharmony_ci rc = h_copy_rdma(cmd->rsp.len, vscsi->dds.window[LOCAL].liobn, 21818c2ecf20Sopenharmony_ci iue->sbuf->dma, vscsi->dds.window[REMOTE].liobn, 21828c2ecf20Sopenharmony_ci be64_to_cpu(iue->remote_token)); 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci switch (rc) { 21858c2ecf20Sopenharmony_ci case H_SUCCESS: 21868c2ecf20Sopenharmony_ci break; 21878c2ecf20Sopenharmony_ci case H_PERMISSION: 21888c2ecf20Sopenharmony_ci if (connection_broken(vscsi)) 21898c2ecf20Sopenharmony_ci flag_bits = RESPONSE_Q_DOWN | CLIENT_FAILED; 21908c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "login_rej: error copying to client, rc %ld\n", 21918c2ecf20Sopenharmony_ci rc); 21928c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 21938c2ecf20Sopenharmony_ci flag_bits); 21948c2ecf20Sopenharmony_ci break; 21958c2ecf20Sopenharmony_ci case H_SOURCE_PARM: 21968c2ecf20Sopenharmony_ci case H_DEST_PARM: 21978c2ecf20Sopenharmony_ci default: 21988c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "login_rej: error copying to client, rc %ld\n", 21998c2ecf20Sopenharmony_ci rc); 22008c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 22018c2ecf20Sopenharmony_ci break; 22028c2ecf20Sopenharmony_ci } 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci return rc; 22058c2ecf20Sopenharmony_ci} 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_cistatic int ibmvscsis_make_nexus(struct ibmvscsis_tport *tport) 22088c2ecf20Sopenharmony_ci{ 22098c2ecf20Sopenharmony_ci char *name = tport->tport_name; 22108c2ecf20Sopenharmony_ci struct ibmvscsis_nexus *nexus; 22118c2ecf20Sopenharmony_ci struct scsi_info *vscsi = container_of(tport, struct scsi_info, tport); 22128c2ecf20Sopenharmony_ci int rc; 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci if (tport->ibmv_nexus) { 22158c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "tport->ibmv_nexus already exists\n"); 22168c2ecf20Sopenharmony_ci return 0; 22178c2ecf20Sopenharmony_ci } 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci nexus = kzalloc(sizeof(*nexus), GFP_KERNEL); 22208c2ecf20Sopenharmony_ci if (!nexus) { 22218c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "Unable to allocate struct ibmvscsis_nexus\n"); 22228c2ecf20Sopenharmony_ci return -ENOMEM; 22238c2ecf20Sopenharmony_ci } 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci nexus->se_sess = target_setup_session(&tport->se_tpg, 0, 0, 22268c2ecf20Sopenharmony_ci TARGET_PROT_NORMAL, name, nexus, 22278c2ecf20Sopenharmony_ci NULL); 22288c2ecf20Sopenharmony_ci if (IS_ERR(nexus->se_sess)) { 22298c2ecf20Sopenharmony_ci rc = PTR_ERR(nexus->se_sess); 22308c2ecf20Sopenharmony_ci goto transport_init_fail; 22318c2ecf20Sopenharmony_ci } 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci tport->ibmv_nexus = nexus; 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci return 0; 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_citransport_init_fail: 22388c2ecf20Sopenharmony_ci kfree(nexus); 22398c2ecf20Sopenharmony_ci return rc; 22408c2ecf20Sopenharmony_ci} 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_cistatic int ibmvscsis_drop_nexus(struct ibmvscsis_tport *tport) 22438c2ecf20Sopenharmony_ci{ 22448c2ecf20Sopenharmony_ci struct se_session *se_sess; 22458c2ecf20Sopenharmony_ci struct ibmvscsis_nexus *nexus; 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci nexus = tport->ibmv_nexus; 22488c2ecf20Sopenharmony_ci if (!nexus) 22498c2ecf20Sopenharmony_ci return -ENODEV; 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci se_sess = nexus->se_sess; 22528c2ecf20Sopenharmony_ci if (!se_sess) 22538c2ecf20Sopenharmony_ci return -ENODEV; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci /* 22568c2ecf20Sopenharmony_ci * Release the SCSI I_T Nexus to the emulated ibmvscsis Target Port 22578c2ecf20Sopenharmony_ci */ 22588c2ecf20Sopenharmony_ci target_remove_session(se_sess); 22598c2ecf20Sopenharmony_ci tport->ibmv_nexus = NULL; 22608c2ecf20Sopenharmony_ci kfree(nexus); 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci return 0; 22638c2ecf20Sopenharmony_ci} 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci/** 22668c2ecf20Sopenharmony_ci * ibmvscsis_srp_login() - Process an SRP Login Request 22678c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 22688c2ecf20Sopenharmony_ci * @cmd: Command element to use to process the SRP Login request 22698c2ecf20Sopenharmony_ci * @crq: Pointer to CRQ entry containing the SRP Login request 22708c2ecf20Sopenharmony_ci * 22718c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 22728c2ecf20Sopenharmony_ci * Interrupt, called with interrupt lock held 22738c2ecf20Sopenharmony_ci */ 22748c2ecf20Sopenharmony_cistatic long ibmvscsis_srp_login(struct scsi_info *vscsi, 22758c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd, 22768c2ecf20Sopenharmony_ci struct viosrp_crq *crq) 22778c2ecf20Sopenharmony_ci{ 22788c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 22798c2ecf20Sopenharmony_ci struct srp_login_req *req = &vio_iu(iue)->srp.login_req; 22808c2ecf20Sopenharmony_ci struct port_id { 22818c2ecf20Sopenharmony_ci __be64 id_extension; 22828c2ecf20Sopenharmony_ci __be64 io_guid; 22838c2ecf20Sopenharmony_ci } *iport, *tport; 22848c2ecf20Sopenharmony_ci struct format_code *fmt; 22858c2ecf20Sopenharmony_ci u32 reason = 0x0; 22868c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci iport = (struct port_id *)req->initiator_port_id; 22898c2ecf20Sopenharmony_ci tport = (struct port_id *)req->target_port_id; 22908c2ecf20Sopenharmony_ci fmt = (struct format_code *)&req->req_buf_fmt; 22918c2ecf20Sopenharmony_ci if (be32_to_cpu(req->req_it_iu_len) > SRP_MAX_IU_LEN) 22928c2ecf20Sopenharmony_ci reason = SRP_LOGIN_REJ_REQ_IT_IU_LENGTH_TOO_LARGE; 22938c2ecf20Sopenharmony_ci else if (be32_to_cpu(req->req_it_iu_len) < 64) 22948c2ecf20Sopenharmony_ci reason = SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL; 22958c2ecf20Sopenharmony_ci else if ((be64_to_cpu(iport->id_extension) > (MAX_NUM_PORTS - 1)) || 22968c2ecf20Sopenharmony_ci (be64_to_cpu(tport->id_extension) > (MAX_NUM_PORTS - 1))) 22978c2ecf20Sopenharmony_ci reason = SRP_LOGIN_REJ_UNABLE_ASSOCIATE_CHANNEL; 22988c2ecf20Sopenharmony_ci else if (req->req_flags & SRP_MULTICHAN_MULTI) 22998c2ecf20Sopenharmony_ci reason = SRP_LOGIN_REJ_MULTI_CHANNEL_UNSUPPORTED; 23008c2ecf20Sopenharmony_ci else if (fmt->buffers & (~SUPPORTED_FORMATS)) 23018c2ecf20Sopenharmony_ci reason = SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT; 23028c2ecf20Sopenharmony_ci else if ((fmt->buffers & SUPPORTED_FORMATS) == 0) 23038c2ecf20Sopenharmony_ci reason = SRP_LOGIN_REJ_UNSUPPORTED_DESCRIPTOR_FMT; 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci if (vscsi->state == SRP_PROCESSING) 23068c2ecf20Sopenharmony_ci reason = SRP_LOGIN_REJ_CHANNEL_LIMIT_REACHED; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci rc = ibmvscsis_make_nexus(&vscsi->tport); 23098c2ecf20Sopenharmony_ci if (rc) 23108c2ecf20Sopenharmony_ci reason = SRP_LOGIN_REJ_UNABLE_ESTABLISH_CHANNEL; 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci cmd->rsp.format = VIOSRP_SRP_FORMAT; 23138c2ecf20Sopenharmony_ci cmd->rsp.tag = req->tag; 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "srp_login: reason 0x%x\n", reason); 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci if (reason) 23188c2ecf20Sopenharmony_ci rc = ibmvscsis_srp_login_rej(vscsi, cmd, reason); 23198c2ecf20Sopenharmony_ci else 23208c2ecf20Sopenharmony_ci rc = ibmvscsis_login_rsp(vscsi, cmd); 23218c2ecf20Sopenharmony_ci 23228c2ecf20Sopenharmony_ci if (!rc) { 23238c2ecf20Sopenharmony_ci if (!reason) 23248c2ecf20Sopenharmony_ci vscsi->state = SRP_PROCESSING; 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci list_add_tail(&cmd->list, &vscsi->waiting_rsp); 23278c2ecf20Sopenharmony_ci ibmvscsis_send_messages(vscsi); 23288c2ecf20Sopenharmony_ci } else { 23298c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, cmd); 23308c2ecf20Sopenharmony_ci } 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Leaving srp_login, rc %ld\n", rc); 23338c2ecf20Sopenharmony_ci return rc; 23348c2ecf20Sopenharmony_ci} 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci/** 23378c2ecf20Sopenharmony_ci * ibmvscsis_srp_i_logout() - Helper Function to close I_T Nexus 23388c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 23398c2ecf20Sopenharmony_ci * @cmd: Command element to use to process the Implicit Logout request 23408c2ecf20Sopenharmony_ci * @crq: Pointer to CRQ entry containing the Implicit Logout request 23418c2ecf20Sopenharmony_ci * 23428c2ecf20Sopenharmony_ci * Do the logic to close the I_T nexus. This function may not 23438c2ecf20Sopenharmony_ci * behave to specification. 23448c2ecf20Sopenharmony_ci * 23458c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 23468c2ecf20Sopenharmony_ci * Interrupt, interrupt lock held 23478c2ecf20Sopenharmony_ci */ 23488c2ecf20Sopenharmony_cistatic long ibmvscsis_srp_i_logout(struct scsi_info *vscsi, 23498c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd, 23508c2ecf20Sopenharmony_ci struct viosrp_crq *crq) 23518c2ecf20Sopenharmony_ci{ 23528c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 23538c2ecf20Sopenharmony_ci struct srp_i_logout *log_out = &vio_iu(iue)->srp.i_logout; 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci if ((vscsi->debit > 0) || !list_empty(&vscsi->schedule_q) || 23568c2ecf20Sopenharmony_ci !list_empty(&vscsi->waiting_rsp)) { 23578c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "i_logout: outstanding work\n"); 23588c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0); 23598c2ecf20Sopenharmony_ci } else { 23608c2ecf20Sopenharmony_ci cmd->rsp.format = SRP_FORMAT; 23618c2ecf20Sopenharmony_ci cmd->rsp.tag = log_out->tag; 23628c2ecf20Sopenharmony_ci cmd->rsp.len = sizeof(struct mad_common); 23638c2ecf20Sopenharmony_ci list_add_tail(&cmd->list, &vscsi->waiting_rsp); 23648c2ecf20Sopenharmony_ci ibmvscsis_send_messages(vscsi); 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 0); 23678c2ecf20Sopenharmony_ci } 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci return ADAPT_SUCCESS; 23708c2ecf20Sopenharmony_ci} 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci/* Called with intr lock held */ 23738c2ecf20Sopenharmony_cistatic void ibmvscsis_srp_cmd(struct scsi_info *vscsi, struct viosrp_crq *crq) 23748c2ecf20Sopenharmony_ci{ 23758c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd; 23768c2ecf20Sopenharmony_ci struct iu_entry *iue; 23778c2ecf20Sopenharmony_ci struct srp_cmd *srp; 23788c2ecf20Sopenharmony_ci struct srp_tsk_mgmt *tsk; 23798c2ecf20Sopenharmony_ci long rc; 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci if (vscsi->request_limit - vscsi->debit <= 0) { 23828c2ecf20Sopenharmony_ci /* Client has exceeded request limit */ 23838c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "Client exceeded the request limit (%d), debit %d\n", 23848c2ecf20Sopenharmony_ci vscsi->request_limit, vscsi->debit); 23858c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 23868c2ecf20Sopenharmony_ci return; 23878c2ecf20Sopenharmony_ci } 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci cmd = ibmvscsis_get_free_cmd(vscsi); 23908c2ecf20Sopenharmony_ci if (!cmd) { 23918c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "srp_cmd failed to get cmd, debit %d\n", 23928c2ecf20Sopenharmony_ci vscsi->debit); 23938c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 23948c2ecf20Sopenharmony_ci return; 23958c2ecf20Sopenharmony_ci } 23968c2ecf20Sopenharmony_ci iue = cmd->iue; 23978c2ecf20Sopenharmony_ci srp = &vio_iu(iue)->srp.cmd; 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci rc = ibmvscsis_copy_crq_packet(vscsi, cmd, crq); 24008c2ecf20Sopenharmony_ci if (rc) { 24018c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, cmd); 24028c2ecf20Sopenharmony_ci return; 24038c2ecf20Sopenharmony_ci } 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci if (vscsi->state == SRP_PROCESSING) { 24068c2ecf20Sopenharmony_ci switch (srp->opcode) { 24078c2ecf20Sopenharmony_ci case SRP_LOGIN_REQ: 24088c2ecf20Sopenharmony_ci rc = ibmvscsis_srp_login(vscsi, cmd, crq); 24098c2ecf20Sopenharmony_ci break; 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_ci case SRP_TSK_MGMT: 24128c2ecf20Sopenharmony_ci tsk = &vio_iu(iue)->srp.tsk_mgmt; 24138c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "tsk_mgmt tag: %llu (0x%llx)\n", 24148c2ecf20Sopenharmony_ci tsk->tag, tsk->tag); 24158c2ecf20Sopenharmony_ci cmd->rsp.tag = tsk->tag; 24168c2ecf20Sopenharmony_ci vscsi->debit += 1; 24178c2ecf20Sopenharmony_ci cmd->type = TASK_MANAGEMENT; 24188c2ecf20Sopenharmony_ci list_add_tail(&cmd->list, &vscsi->schedule_q); 24198c2ecf20Sopenharmony_ci queue_work(vscsi->work_q, &cmd->work); 24208c2ecf20Sopenharmony_ci break; 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci case SRP_CMD: 24238c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "srp_cmd tag: %llu (0x%llx)\n", 24248c2ecf20Sopenharmony_ci srp->tag, srp->tag); 24258c2ecf20Sopenharmony_ci cmd->rsp.tag = srp->tag; 24268c2ecf20Sopenharmony_ci vscsi->debit += 1; 24278c2ecf20Sopenharmony_ci cmd->type = SCSI_CDB; 24288c2ecf20Sopenharmony_ci /* 24298c2ecf20Sopenharmony_ci * We want to keep track of work waiting for 24308c2ecf20Sopenharmony_ci * the workqueue. 24318c2ecf20Sopenharmony_ci */ 24328c2ecf20Sopenharmony_ci list_add_tail(&cmd->list, &vscsi->schedule_q); 24338c2ecf20Sopenharmony_ci queue_work(vscsi->work_q, &cmd->work); 24348c2ecf20Sopenharmony_ci break; 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci case SRP_I_LOGOUT: 24378c2ecf20Sopenharmony_ci rc = ibmvscsis_srp_i_logout(vscsi, cmd, crq); 24388c2ecf20Sopenharmony_ci break; 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci case SRP_CRED_RSP: 24418c2ecf20Sopenharmony_ci case SRP_AER_RSP: 24428c2ecf20Sopenharmony_ci default: 24438c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, cmd); 24448c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "invalid srp cmd, opcode %d\n", 24458c2ecf20Sopenharmony_ci (uint)srp->opcode); 24468c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, 24478c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT, 0); 24488c2ecf20Sopenharmony_ci break; 24498c2ecf20Sopenharmony_ci } 24508c2ecf20Sopenharmony_ci } else if (srp->opcode == SRP_LOGIN_REQ && vscsi->state == CONNECTED) { 24518c2ecf20Sopenharmony_ci rc = ibmvscsis_srp_login(vscsi, cmd, crq); 24528c2ecf20Sopenharmony_ci } else { 24538c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, cmd); 24548c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "Invalid state %d to handle srp cmd\n", 24558c2ecf20Sopenharmony_ci vscsi->state); 24568c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 24578c2ecf20Sopenharmony_ci } 24588c2ecf20Sopenharmony_ci} 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci/** 24618c2ecf20Sopenharmony_ci * ibmvscsis_ping_response() - Respond to a ping request 24628c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 24638c2ecf20Sopenharmony_ci * 24648c2ecf20Sopenharmony_ci * Let the client know that the server is alive and waiting on 24658c2ecf20Sopenharmony_ci * its native I/O stack. 24668c2ecf20Sopenharmony_ci * If any type of error occurs from the call to queue a ping 24678c2ecf20Sopenharmony_ci * response then the client is either not accepting or receiving 24688c2ecf20Sopenharmony_ci * interrupts. Disconnect with an error. 24698c2ecf20Sopenharmony_ci * 24708c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 24718c2ecf20Sopenharmony_ci * Interrupt, interrupt lock held 24728c2ecf20Sopenharmony_ci */ 24738c2ecf20Sopenharmony_cistatic long ibmvscsis_ping_response(struct scsi_info *vscsi) 24748c2ecf20Sopenharmony_ci{ 24758c2ecf20Sopenharmony_ci struct viosrp_crq *crq; 24768c2ecf20Sopenharmony_ci u64 buffer[2] = { 0, 0 }; 24778c2ecf20Sopenharmony_ci long rc; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci crq = (struct viosrp_crq *)&buffer; 24808c2ecf20Sopenharmony_ci crq->valid = VALID_CMD_RESP_EL; 24818c2ecf20Sopenharmony_ci crq->format = (u8)MESSAGE_IN_CRQ; 24828c2ecf20Sopenharmony_ci crq->status = PING_RESPONSE; 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci rc = h_send_crq(vscsi->dds.unit_id, cpu_to_be64(buffer[MSG_HI]), 24858c2ecf20Sopenharmony_ci cpu_to_be64(buffer[MSG_LOW])); 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci switch (rc) { 24888c2ecf20Sopenharmony_ci case H_SUCCESS: 24898c2ecf20Sopenharmony_ci break; 24908c2ecf20Sopenharmony_ci case H_CLOSED: 24918c2ecf20Sopenharmony_ci vscsi->flags |= CLIENT_FAILED; 24928c2ecf20Sopenharmony_ci fallthrough; 24938c2ecf20Sopenharmony_ci case H_DROPPED: 24948c2ecf20Sopenharmony_ci vscsi->flags |= RESPONSE_Q_DOWN; 24958c2ecf20Sopenharmony_ci fallthrough; 24968c2ecf20Sopenharmony_ci case H_REMOTE_PARM: 24978c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "ping_response: h_send_crq failed, rc %ld\n", 24988c2ecf20Sopenharmony_ci rc); 24998c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 25008c2ecf20Sopenharmony_ci break; 25018c2ecf20Sopenharmony_ci default: 25028c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "ping_response: h_send_crq returned unknown rc %ld\n", 25038c2ecf20Sopenharmony_ci rc); 25048c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0); 25058c2ecf20Sopenharmony_ci break; 25068c2ecf20Sopenharmony_ci } 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci return rc; 25098c2ecf20Sopenharmony_ci} 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci/** 25128c2ecf20Sopenharmony_ci * ibmvscsis_parse_command() - Parse an element taken from the cmd rsp queue. 25138c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 25148c2ecf20Sopenharmony_ci * @crq: Pointer to CRQ element containing the SRP request 25158c2ecf20Sopenharmony_ci * 25168c2ecf20Sopenharmony_ci * This function will return success if the command queue element is valid 25178c2ecf20Sopenharmony_ci * and the srp iu or MAD request it pointed to was also valid. That does 25188c2ecf20Sopenharmony_ci * not mean that an error was not returned to the client. 25198c2ecf20Sopenharmony_ci * 25208c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 25218c2ecf20Sopenharmony_ci * Interrupt, intr lock held 25228c2ecf20Sopenharmony_ci */ 25238c2ecf20Sopenharmony_cistatic long ibmvscsis_parse_command(struct scsi_info *vscsi, 25248c2ecf20Sopenharmony_ci struct viosrp_crq *crq) 25258c2ecf20Sopenharmony_ci{ 25268c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci switch (crq->valid) { 25298c2ecf20Sopenharmony_ci case VALID_CMD_RESP_EL: 25308c2ecf20Sopenharmony_ci switch (crq->format) { 25318c2ecf20Sopenharmony_ci case OS400_FORMAT: 25328c2ecf20Sopenharmony_ci case AIX_FORMAT: 25338c2ecf20Sopenharmony_ci case LINUX_FORMAT: 25348c2ecf20Sopenharmony_ci case MAD_FORMAT: 25358c2ecf20Sopenharmony_ci if (vscsi->flags & PROCESSING_MAD) { 25368c2ecf20Sopenharmony_ci rc = ERROR; 25378c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "parse_command: already processing mad\n"); 25388c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, 25398c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT, 25408c2ecf20Sopenharmony_ci 0); 25418c2ecf20Sopenharmony_ci } else { 25428c2ecf20Sopenharmony_ci vscsi->flags |= PROCESSING_MAD; 25438c2ecf20Sopenharmony_ci rc = ibmvscsis_mad(vscsi, crq); 25448c2ecf20Sopenharmony_ci } 25458c2ecf20Sopenharmony_ci break; 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci case SRP_FORMAT: 25488c2ecf20Sopenharmony_ci ibmvscsis_srp_cmd(vscsi, crq); 25498c2ecf20Sopenharmony_ci break; 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_ci case MESSAGE_IN_CRQ: 25528c2ecf20Sopenharmony_ci if (crq->status == PING) 25538c2ecf20Sopenharmony_ci ibmvscsis_ping_response(vscsi); 25548c2ecf20Sopenharmony_ci break; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci default: 25578c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "parse_command: invalid format %d\n", 25588c2ecf20Sopenharmony_ci (uint)crq->format); 25598c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, 25608c2ecf20Sopenharmony_ci ERR_DISCONNECT_RECONNECT, 0); 25618c2ecf20Sopenharmony_ci break; 25628c2ecf20Sopenharmony_ci } 25638c2ecf20Sopenharmony_ci break; 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_ci case VALID_TRANS_EVENT: 25668c2ecf20Sopenharmony_ci rc = ibmvscsis_trans_event(vscsi, crq); 25678c2ecf20Sopenharmony_ci break; 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci case VALID_INIT_MSG: 25708c2ecf20Sopenharmony_ci rc = ibmvscsis_init_msg(vscsi, crq); 25718c2ecf20Sopenharmony_ci break; 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci default: 25748c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "parse_command: invalid valid field %d\n", 25758c2ecf20Sopenharmony_ci (uint)crq->valid); 25768c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 25778c2ecf20Sopenharmony_ci break; 25788c2ecf20Sopenharmony_ci } 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci /* 25818c2ecf20Sopenharmony_ci * Return only what the interrupt handler cares 25828c2ecf20Sopenharmony_ci * about. Most errors we keep right on trucking. 25838c2ecf20Sopenharmony_ci */ 25848c2ecf20Sopenharmony_ci rc = vscsi->flags & SCHEDULE_DISCONNECT; 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci return rc; 25878c2ecf20Sopenharmony_ci} 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_cistatic int read_dma_window(struct scsi_info *vscsi) 25908c2ecf20Sopenharmony_ci{ 25918c2ecf20Sopenharmony_ci struct vio_dev *vdev = vscsi->dma_dev; 25928c2ecf20Sopenharmony_ci const __be32 *dma_window; 25938c2ecf20Sopenharmony_ci const __be32 *prop; 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ci /* TODO Using of_parse_dma_window would be better, but it doesn't give 25968c2ecf20Sopenharmony_ci * a way to read multiple windows without already knowing the size of 25978c2ecf20Sopenharmony_ci * a window or the number of windows. 25988c2ecf20Sopenharmony_ci */ 25998c2ecf20Sopenharmony_ci dma_window = (const __be32 *)vio_get_attribute(vdev, 26008c2ecf20Sopenharmony_ci "ibm,my-dma-window", 26018c2ecf20Sopenharmony_ci NULL); 26028c2ecf20Sopenharmony_ci if (!dma_window) { 26038c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "Couldn't find ibm,my-dma-window property\n"); 26048c2ecf20Sopenharmony_ci return -1; 26058c2ecf20Sopenharmony_ci } 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci vscsi->dds.window[LOCAL].liobn = be32_to_cpu(*dma_window); 26088c2ecf20Sopenharmony_ci dma_window++; 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-address-cells", 26118c2ecf20Sopenharmony_ci NULL); 26128c2ecf20Sopenharmony_ci if (!prop) { 26138c2ecf20Sopenharmony_ci dev_warn(&vscsi->dev, "Couldn't find ibm,#dma-address-cells property\n"); 26148c2ecf20Sopenharmony_ci dma_window++; 26158c2ecf20Sopenharmony_ci } else { 26168c2ecf20Sopenharmony_ci dma_window += be32_to_cpu(*prop); 26178c2ecf20Sopenharmony_ci } 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-size-cells", 26208c2ecf20Sopenharmony_ci NULL); 26218c2ecf20Sopenharmony_ci if (!prop) { 26228c2ecf20Sopenharmony_ci dev_warn(&vscsi->dev, "Couldn't find ibm,#dma-size-cells property\n"); 26238c2ecf20Sopenharmony_ci dma_window++; 26248c2ecf20Sopenharmony_ci } else { 26258c2ecf20Sopenharmony_ci dma_window += be32_to_cpu(*prop); 26268c2ecf20Sopenharmony_ci } 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_ci /* dma_window should point to the second window now */ 26298c2ecf20Sopenharmony_ci vscsi->dds.window[REMOTE].liobn = be32_to_cpu(*dma_window); 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci return 0; 26328c2ecf20Sopenharmony_ci} 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_cistatic struct ibmvscsis_tport *ibmvscsis_lookup_port(const char *name) 26358c2ecf20Sopenharmony_ci{ 26368c2ecf20Sopenharmony_ci struct ibmvscsis_tport *tport = NULL; 26378c2ecf20Sopenharmony_ci struct vio_dev *vdev; 26388c2ecf20Sopenharmony_ci struct scsi_info *vscsi; 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci spin_lock_bh(&ibmvscsis_dev_lock); 26418c2ecf20Sopenharmony_ci list_for_each_entry(vscsi, &ibmvscsis_dev_list, list) { 26428c2ecf20Sopenharmony_ci vdev = vscsi->dma_dev; 26438c2ecf20Sopenharmony_ci if (!strcmp(dev_name(&vdev->dev), name)) { 26448c2ecf20Sopenharmony_ci tport = &vscsi->tport; 26458c2ecf20Sopenharmony_ci break; 26468c2ecf20Sopenharmony_ci } 26478c2ecf20Sopenharmony_ci } 26488c2ecf20Sopenharmony_ci spin_unlock_bh(&ibmvscsis_dev_lock); 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_ci return tport; 26518c2ecf20Sopenharmony_ci} 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ci/** 26548c2ecf20Sopenharmony_ci * ibmvscsis_parse_cmd() - Parse SRP Command 26558c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 26568c2ecf20Sopenharmony_ci * @cmd: Pointer to command element with SRP command 26578c2ecf20Sopenharmony_ci * 26588c2ecf20Sopenharmony_ci * Parse the srp command; if it is valid then submit it to tcm. 26598c2ecf20Sopenharmony_ci * Note: The return code does not reflect the status of the SCSI CDB. 26608c2ecf20Sopenharmony_ci * 26618c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 26628c2ecf20Sopenharmony_ci * Process level 26638c2ecf20Sopenharmony_ci */ 26648c2ecf20Sopenharmony_cistatic void ibmvscsis_parse_cmd(struct scsi_info *vscsi, 26658c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd) 26668c2ecf20Sopenharmony_ci{ 26678c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 26688c2ecf20Sopenharmony_ci struct srp_cmd *srp = (struct srp_cmd *)iue->sbuf->buf; 26698c2ecf20Sopenharmony_ci struct ibmvscsis_nexus *nexus; 26708c2ecf20Sopenharmony_ci u64 data_len = 0; 26718c2ecf20Sopenharmony_ci enum dma_data_direction dir; 26728c2ecf20Sopenharmony_ci int attr = 0; 26738c2ecf20Sopenharmony_ci int rc = 0; 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci nexus = vscsi->tport.ibmv_nexus; 26768c2ecf20Sopenharmony_ci /* 26778c2ecf20Sopenharmony_ci * additional length in bytes. Note that the SRP spec says that 26788c2ecf20Sopenharmony_ci * additional length is in 4-byte words, but technically the 26798c2ecf20Sopenharmony_ci * additional length field is only the upper 6 bits of the byte. 26808c2ecf20Sopenharmony_ci * The lower 2 bits are reserved. If the lower 2 bits are 0 (as 26818c2ecf20Sopenharmony_ci * all reserved fields should be), then interpreting the byte as 26828c2ecf20Sopenharmony_ci * an int will yield the length in bytes. 26838c2ecf20Sopenharmony_ci */ 26848c2ecf20Sopenharmony_ci if (srp->add_cdb_len & 0x03) { 26858c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "parse_cmd: reserved bits set in IU\n"); 26868c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 26878c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 26888c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, cmd); 26898c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 26908c2ecf20Sopenharmony_ci return; 26918c2ecf20Sopenharmony_ci } 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_ci if (srp_get_desc_table(srp, &dir, &data_len)) { 26948c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "0x%llx: parsing SRP descriptor table failed.\n", 26958c2ecf20Sopenharmony_ci srp->tag); 26968c2ecf20Sopenharmony_ci goto fail; 26978c2ecf20Sopenharmony_ci } 26988c2ecf20Sopenharmony_ci 26998c2ecf20Sopenharmony_ci cmd->rsp.sol_not = srp->sol_not; 27008c2ecf20Sopenharmony_ci 27018c2ecf20Sopenharmony_ci switch (srp->task_attr) { 27028c2ecf20Sopenharmony_ci case SRP_SIMPLE_TASK: 27038c2ecf20Sopenharmony_ci attr = TCM_SIMPLE_TAG; 27048c2ecf20Sopenharmony_ci break; 27058c2ecf20Sopenharmony_ci case SRP_ORDERED_TASK: 27068c2ecf20Sopenharmony_ci attr = TCM_ORDERED_TAG; 27078c2ecf20Sopenharmony_ci break; 27088c2ecf20Sopenharmony_ci case SRP_HEAD_TASK: 27098c2ecf20Sopenharmony_ci attr = TCM_HEAD_TAG; 27108c2ecf20Sopenharmony_ci break; 27118c2ecf20Sopenharmony_ci case SRP_ACA_TASK: 27128c2ecf20Sopenharmony_ci attr = TCM_ACA_TAG; 27138c2ecf20Sopenharmony_ci break; 27148c2ecf20Sopenharmony_ci default: 27158c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "Invalid task attribute %d\n", 27168c2ecf20Sopenharmony_ci srp->task_attr); 27178c2ecf20Sopenharmony_ci goto fail; 27188c2ecf20Sopenharmony_ci } 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci cmd->se_cmd.tag = be64_to_cpu(srp->tag); 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 27238c2ecf20Sopenharmony_ci list_add_tail(&cmd->list, &vscsi->active_q); 27248c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_ci srp->lun.scsi_lun[0] &= 0x3f; 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_ci rc = target_submit_cmd(&cmd->se_cmd, nexus->se_sess, srp->cdb, 27298c2ecf20Sopenharmony_ci cmd->sense_buf, scsilun_to_int(&srp->lun), 27308c2ecf20Sopenharmony_ci data_len, attr, dir, 0); 27318c2ecf20Sopenharmony_ci if (rc) { 27328c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "target_submit_cmd failed, rc %d\n", rc); 27338c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 27348c2ecf20Sopenharmony_ci list_del(&cmd->list); 27358c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, cmd); 27368c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 27378c2ecf20Sopenharmony_ci goto fail; 27388c2ecf20Sopenharmony_ci } 27398c2ecf20Sopenharmony_ci return; 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_cifail: 27428c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 27438c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); 27448c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 27458c2ecf20Sopenharmony_ci} 27468c2ecf20Sopenharmony_ci 27478c2ecf20Sopenharmony_ci/** 27488c2ecf20Sopenharmony_ci * ibmvscsis_parse_task() - Parse SRP Task Management Request 27498c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 27508c2ecf20Sopenharmony_ci * @cmd: Pointer to command element with SRP task management request 27518c2ecf20Sopenharmony_ci * 27528c2ecf20Sopenharmony_ci * Parse the srp task management request; if it is valid then submit it to tcm. 27538c2ecf20Sopenharmony_ci * Note: The return code does not reflect the status of the task management 27548c2ecf20Sopenharmony_ci * request. 27558c2ecf20Sopenharmony_ci * 27568c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 27578c2ecf20Sopenharmony_ci * Processor level 27588c2ecf20Sopenharmony_ci */ 27598c2ecf20Sopenharmony_cistatic void ibmvscsis_parse_task(struct scsi_info *vscsi, 27608c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd) 27618c2ecf20Sopenharmony_ci{ 27628c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 27638c2ecf20Sopenharmony_ci struct srp_tsk_mgmt *srp_tsk = &vio_iu(iue)->srp.tsk_mgmt; 27648c2ecf20Sopenharmony_ci int tcm_type; 27658c2ecf20Sopenharmony_ci u64 tag_to_abort = 0; 27668c2ecf20Sopenharmony_ci int rc = 0; 27678c2ecf20Sopenharmony_ci struct ibmvscsis_nexus *nexus; 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_ci nexus = vscsi->tport.ibmv_nexus; 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci cmd->rsp.sol_not = srp_tsk->sol_not; 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci switch (srp_tsk->tsk_mgmt_func) { 27748c2ecf20Sopenharmony_ci case SRP_TSK_ABORT_TASK: 27758c2ecf20Sopenharmony_ci tcm_type = TMR_ABORT_TASK; 27768c2ecf20Sopenharmony_ci tag_to_abort = be64_to_cpu(srp_tsk->task_tag); 27778c2ecf20Sopenharmony_ci break; 27788c2ecf20Sopenharmony_ci case SRP_TSK_ABORT_TASK_SET: 27798c2ecf20Sopenharmony_ci tcm_type = TMR_ABORT_TASK_SET; 27808c2ecf20Sopenharmony_ci break; 27818c2ecf20Sopenharmony_ci case SRP_TSK_CLEAR_TASK_SET: 27828c2ecf20Sopenharmony_ci tcm_type = TMR_CLEAR_TASK_SET; 27838c2ecf20Sopenharmony_ci break; 27848c2ecf20Sopenharmony_ci case SRP_TSK_LUN_RESET: 27858c2ecf20Sopenharmony_ci tcm_type = TMR_LUN_RESET; 27868c2ecf20Sopenharmony_ci break; 27878c2ecf20Sopenharmony_ci case SRP_TSK_CLEAR_ACA: 27888c2ecf20Sopenharmony_ci tcm_type = TMR_CLEAR_ACA; 27898c2ecf20Sopenharmony_ci break; 27908c2ecf20Sopenharmony_ci default: 27918c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "unknown task mgmt func %d\n", 27928c2ecf20Sopenharmony_ci srp_tsk->tsk_mgmt_func); 27938c2ecf20Sopenharmony_ci cmd->se_cmd.se_tmr_req->response = 27948c2ecf20Sopenharmony_ci TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED; 27958c2ecf20Sopenharmony_ci rc = -1; 27968c2ecf20Sopenharmony_ci break; 27978c2ecf20Sopenharmony_ci } 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci if (!rc) { 28008c2ecf20Sopenharmony_ci cmd->se_cmd.tag = be64_to_cpu(srp_tsk->tag); 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 28038c2ecf20Sopenharmony_ci list_add_tail(&cmd->list, &vscsi->active_q); 28048c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 28058c2ecf20Sopenharmony_ci 28068c2ecf20Sopenharmony_ci srp_tsk->lun.scsi_lun[0] &= 0x3f; 28078c2ecf20Sopenharmony_ci 28088c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "calling submit_tmr, func %d\n", 28098c2ecf20Sopenharmony_ci srp_tsk->tsk_mgmt_func); 28108c2ecf20Sopenharmony_ci rc = target_submit_tmr(&cmd->se_cmd, nexus->se_sess, NULL, 28118c2ecf20Sopenharmony_ci scsilun_to_int(&srp_tsk->lun), srp_tsk, 28128c2ecf20Sopenharmony_ci tcm_type, GFP_KERNEL, tag_to_abort, 0); 28138c2ecf20Sopenharmony_ci if (rc) { 28148c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "target_submit_tmr failed, rc %d\n", 28158c2ecf20Sopenharmony_ci rc); 28168c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 28178c2ecf20Sopenharmony_ci list_del(&cmd->list); 28188c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 28198c2ecf20Sopenharmony_ci cmd->se_cmd.se_tmr_req->response = 28208c2ecf20Sopenharmony_ci TMR_FUNCTION_REJECTED; 28218c2ecf20Sopenharmony_ci } 28228c2ecf20Sopenharmony_ci } 28238c2ecf20Sopenharmony_ci 28248c2ecf20Sopenharmony_ci if (rc) 28258c2ecf20Sopenharmony_ci transport_send_check_condition_and_sense(&cmd->se_cmd, 0, 0); 28268c2ecf20Sopenharmony_ci} 28278c2ecf20Sopenharmony_ci 28288c2ecf20Sopenharmony_cistatic void ibmvscsis_scheduler(struct work_struct *work) 28298c2ecf20Sopenharmony_ci{ 28308c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd = container_of(work, struct ibmvscsis_cmd, 28318c2ecf20Sopenharmony_ci work); 28328c2ecf20Sopenharmony_ci struct scsi_info *vscsi = cmd->adapter; 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci /* Remove from schedule_q */ 28378c2ecf20Sopenharmony_ci list_del(&cmd->list); 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_ci /* Don't submit cmd if we're disconnecting */ 28408c2ecf20Sopenharmony_ci if (vscsi->flags & (SCHEDULE_DISCONNECT | DISCONNECT_SCHEDULED)) { 28418c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, cmd); 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci /* ibmvscsis_disconnect might be waiting for us */ 28448c2ecf20Sopenharmony_ci if (list_empty(&vscsi->active_q) && 28458c2ecf20Sopenharmony_ci list_empty(&vscsi->schedule_q) && 28468c2ecf20Sopenharmony_ci (vscsi->flags & WAIT_FOR_IDLE)) { 28478c2ecf20Sopenharmony_ci vscsi->flags &= ~WAIT_FOR_IDLE; 28488c2ecf20Sopenharmony_ci complete(&vscsi->wait_idle); 28498c2ecf20Sopenharmony_ci } 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 28528c2ecf20Sopenharmony_ci return; 28538c2ecf20Sopenharmony_ci } 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 28568c2ecf20Sopenharmony_ci 28578c2ecf20Sopenharmony_ci switch (cmd->type) { 28588c2ecf20Sopenharmony_ci case SCSI_CDB: 28598c2ecf20Sopenharmony_ci ibmvscsis_parse_cmd(vscsi, cmd); 28608c2ecf20Sopenharmony_ci break; 28618c2ecf20Sopenharmony_ci case TASK_MANAGEMENT: 28628c2ecf20Sopenharmony_ci ibmvscsis_parse_task(vscsi, cmd); 28638c2ecf20Sopenharmony_ci break; 28648c2ecf20Sopenharmony_ci default: 28658c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "scheduler, invalid cmd type %d\n", 28668c2ecf20Sopenharmony_ci cmd->type); 28678c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 28688c2ecf20Sopenharmony_ci ibmvscsis_free_cmd_resources(vscsi, cmd); 28698c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 28708c2ecf20Sopenharmony_ci break; 28718c2ecf20Sopenharmony_ci } 28728c2ecf20Sopenharmony_ci} 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_cistatic int ibmvscsis_alloc_cmds(struct scsi_info *vscsi, int num) 28758c2ecf20Sopenharmony_ci{ 28768c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd; 28778c2ecf20Sopenharmony_ci int i; 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vscsi->free_cmd); 28808c2ecf20Sopenharmony_ci vscsi->cmd_pool = kcalloc(num, sizeof(struct ibmvscsis_cmd), 28818c2ecf20Sopenharmony_ci GFP_KERNEL); 28828c2ecf20Sopenharmony_ci if (!vscsi->cmd_pool) 28838c2ecf20Sopenharmony_ci return -ENOMEM; 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci for (i = 0, cmd = (struct ibmvscsis_cmd *)vscsi->cmd_pool; i < num; 28868c2ecf20Sopenharmony_ci i++, cmd++) { 28878c2ecf20Sopenharmony_ci cmd->abort_cmd = NULL; 28888c2ecf20Sopenharmony_ci cmd->adapter = vscsi; 28898c2ecf20Sopenharmony_ci INIT_WORK(&cmd->work, ibmvscsis_scheduler); 28908c2ecf20Sopenharmony_ci list_add_tail(&cmd->list, &vscsi->free_cmd); 28918c2ecf20Sopenharmony_ci } 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_ci return 0; 28948c2ecf20Sopenharmony_ci} 28958c2ecf20Sopenharmony_ci 28968c2ecf20Sopenharmony_cistatic void ibmvscsis_free_cmds(struct scsi_info *vscsi) 28978c2ecf20Sopenharmony_ci{ 28988c2ecf20Sopenharmony_ci kfree(vscsi->cmd_pool); 28998c2ecf20Sopenharmony_ci vscsi->cmd_pool = NULL; 29008c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vscsi->free_cmd); 29018c2ecf20Sopenharmony_ci} 29028c2ecf20Sopenharmony_ci 29038c2ecf20Sopenharmony_ci/** 29048c2ecf20Sopenharmony_ci * ibmvscsis_service_wait_q() - Service Waiting Queue 29058c2ecf20Sopenharmony_ci * @timer: Pointer to timer which has expired 29068c2ecf20Sopenharmony_ci * 29078c2ecf20Sopenharmony_ci * This routine is called when the timer pops to service the waiting 29088c2ecf20Sopenharmony_ci * queue. Elements on the queue have completed, their responses have been 29098c2ecf20Sopenharmony_ci * copied to the client, but the client's response queue was full so 29108c2ecf20Sopenharmony_ci * the queue message could not be sent. The routine grabs the proper locks 29118c2ecf20Sopenharmony_ci * and calls send messages. 29128c2ecf20Sopenharmony_ci * 29138c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 29148c2ecf20Sopenharmony_ci * called at interrupt level 29158c2ecf20Sopenharmony_ci */ 29168c2ecf20Sopenharmony_cistatic enum hrtimer_restart ibmvscsis_service_wait_q(struct hrtimer *timer) 29178c2ecf20Sopenharmony_ci{ 29188c2ecf20Sopenharmony_ci struct timer_cb *p_timer = container_of(timer, struct timer_cb, timer); 29198c2ecf20Sopenharmony_ci struct scsi_info *vscsi = container_of(p_timer, struct scsi_info, 29208c2ecf20Sopenharmony_ci rsp_q_timer); 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 29238c2ecf20Sopenharmony_ci p_timer->timer_pops += 1; 29248c2ecf20Sopenharmony_ci p_timer->started = false; 29258c2ecf20Sopenharmony_ci ibmvscsis_send_messages(vscsi); 29268c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_ci return HRTIMER_NORESTART; 29298c2ecf20Sopenharmony_ci} 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_cistatic long ibmvscsis_alloctimer(struct scsi_info *vscsi) 29328c2ecf20Sopenharmony_ci{ 29338c2ecf20Sopenharmony_ci struct timer_cb *p_timer; 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_ci p_timer = &vscsi->rsp_q_timer; 29368c2ecf20Sopenharmony_ci hrtimer_init(&p_timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci p_timer->timer.function = ibmvscsis_service_wait_q; 29398c2ecf20Sopenharmony_ci p_timer->started = false; 29408c2ecf20Sopenharmony_ci p_timer->timer_pops = 0; 29418c2ecf20Sopenharmony_ci 29428c2ecf20Sopenharmony_ci return ADAPT_SUCCESS; 29438c2ecf20Sopenharmony_ci} 29448c2ecf20Sopenharmony_ci 29458c2ecf20Sopenharmony_cistatic void ibmvscsis_freetimer(struct scsi_info *vscsi) 29468c2ecf20Sopenharmony_ci{ 29478c2ecf20Sopenharmony_ci struct timer_cb *p_timer; 29488c2ecf20Sopenharmony_ci 29498c2ecf20Sopenharmony_ci p_timer = &vscsi->rsp_q_timer; 29508c2ecf20Sopenharmony_ci 29518c2ecf20Sopenharmony_ci (void)hrtimer_cancel(&p_timer->timer); 29528c2ecf20Sopenharmony_ci 29538c2ecf20Sopenharmony_ci p_timer->started = false; 29548c2ecf20Sopenharmony_ci p_timer->timer_pops = 0; 29558c2ecf20Sopenharmony_ci} 29568c2ecf20Sopenharmony_ci 29578c2ecf20Sopenharmony_cistatic irqreturn_t ibmvscsis_interrupt(int dummy, void *data) 29588c2ecf20Sopenharmony_ci{ 29598c2ecf20Sopenharmony_ci struct scsi_info *vscsi = data; 29608c2ecf20Sopenharmony_ci 29618c2ecf20Sopenharmony_ci vio_disable_interrupts(vscsi->dma_dev); 29628c2ecf20Sopenharmony_ci tasklet_schedule(&vscsi->work_task); 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_ci return IRQ_HANDLED; 29658c2ecf20Sopenharmony_ci} 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_ci/** 29688c2ecf20Sopenharmony_ci * ibmvscsis_enable_change_state() - Set new state based on enabled status 29698c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 29708c2ecf20Sopenharmony_ci * 29718c2ecf20Sopenharmony_ci * This function determines our new state now that we are enabled. This 29728c2ecf20Sopenharmony_ci * may involve sending an Init Complete message to the client. 29738c2ecf20Sopenharmony_ci * 29748c2ecf20Sopenharmony_ci * Must be called with interrupt lock held. 29758c2ecf20Sopenharmony_ci */ 29768c2ecf20Sopenharmony_cistatic long ibmvscsis_enable_change_state(struct scsi_info *vscsi) 29778c2ecf20Sopenharmony_ci{ 29788c2ecf20Sopenharmony_ci int bytes; 29798c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci bytes = vscsi->cmd_q.size * PAGE_SIZE; 29828c2ecf20Sopenharmony_ci rc = h_reg_crq(vscsi->dds.unit_id, vscsi->cmd_q.crq_token, bytes); 29838c2ecf20Sopenharmony_ci if (rc == H_CLOSED || rc == H_SUCCESS) { 29848c2ecf20Sopenharmony_ci vscsi->state = WAIT_CONNECTION; 29858c2ecf20Sopenharmony_ci rc = ibmvscsis_establish_new_q(vscsi); 29868c2ecf20Sopenharmony_ci } 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_ci if (rc != ADAPT_SUCCESS) { 29898c2ecf20Sopenharmony_ci vscsi->state = ERR_DISCONNECTED; 29908c2ecf20Sopenharmony_ci vscsi->flags |= RESPONSE_Q_DOWN; 29918c2ecf20Sopenharmony_ci } 29928c2ecf20Sopenharmony_ci 29938c2ecf20Sopenharmony_ci return rc; 29948c2ecf20Sopenharmony_ci} 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_ci/** 29978c2ecf20Sopenharmony_ci * ibmvscsis_create_command_q() - Create Command Queue 29988c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 29998c2ecf20Sopenharmony_ci * @num_cmds: Currently unused. In the future, may be used to determine 30008c2ecf20Sopenharmony_ci * the size of the CRQ. 30018c2ecf20Sopenharmony_ci * 30028c2ecf20Sopenharmony_ci * Allocates memory for command queue maps remote memory into an ioba 30038c2ecf20Sopenharmony_ci * initializes the command response queue 30048c2ecf20Sopenharmony_ci * 30058c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 30068c2ecf20Sopenharmony_ci * Process level only 30078c2ecf20Sopenharmony_ci */ 30088c2ecf20Sopenharmony_cistatic long ibmvscsis_create_command_q(struct scsi_info *vscsi, int num_cmds) 30098c2ecf20Sopenharmony_ci{ 30108c2ecf20Sopenharmony_ci int pages; 30118c2ecf20Sopenharmony_ci struct vio_dev *vdev = vscsi->dma_dev; 30128c2ecf20Sopenharmony_ci 30138c2ecf20Sopenharmony_ci /* We might support multiple pages in the future, but just 1 for now */ 30148c2ecf20Sopenharmony_ci pages = 1; 30158c2ecf20Sopenharmony_ci 30168c2ecf20Sopenharmony_ci vscsi->cmd_q.size = pages; 30178c2ecf20Sopenharmony_ci 30188c2ecf20Sopenharmony_ci vscsi->cmd_q.base_addr = 30198c2ecf20Sopenharmony_ci (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL); 30208c2ecf20Sopenharmony_ci if (!vscsi->cmd_q.base_addr) 30218c2ecf20Sopenharmony_ci return -ENOMEM; 30228c2ecf20Sopenharmony_ci 30238c2ecf20Sopenharmony_ci vscsi->cmd_q.mask = ((uint)pages * CRQ_PER_PAGE) - 1; 30248c2ecf20Sopenharmony_ci 30258c2ecf20Sopenharmony_ci vscsi->cmd_q.crq_token = dma_map_single(&vdev->dev, 30268c2ecf20Sopenharmony_ci vscsi->cmd_q.base_addr, 30278c2ecf20Sopenharmony_ci PAGE_SIZE, DMA_BIDIRECTIONAL); 30288c2ecf20Sopenharmony_ci if (dma_mapping_error(&vdev->dev, vscsi->cmd_q.crq_token)) { 30298c2ecf20Sopenharmony_ci free_page((unsigned long)vscsi->cmd_q.base_addr); 30308c2ecf20Sopenharmony_ci return -ENOMEM; 30318c2ecf20Sopenharmony_ci } 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ci return 0; 30348c2ecf20Sopenharmony_ci} 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci/** 30378c2ecf20Sopenharmony_ci * ibmvscsis_destroy_command_q - Destroy Command Queue 30388c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 30398c2ecf20Sopenharmony_ci * 30408c2ecf20Sopenharmony_ci * Releases memory for command queue and unmaps mapped remote memory. 30418c2ecf20Sopenharmony_ci * 30428c2ecf20Sopenharmony_ci * EXECUTION ENVIRONMENT: 30438c2ecf20Sopenharmony_ci * Process level only 30448c2ecf20Sopenharmony_ci */ 30458c2ecf20Sopenharmony_cistatic void ibmvscsis_destroy_command_q(struct scsi_info *vscsi) 30468c2ecf20Sopenharmony_ci{ 30478c2ecf20Sopenharmony_ci dma_unmap_single(&vscsi->dma_dev->dev, vscsi->cmd_q.crq_token, 30488c2ecf20Sopenharmony_ci PAGE_SIZE, DMA_BIDIRECTIONAL); 30498c2ecf20Sopenharmony_ci free_page((unsigned long)vscsi->cmd_q.base_addr); 30508c2ecf20Sopenharmony_ci vscsi->cmd_q.base_addr = NULL; 30518c2ecf20Sopenharmony_ci vscsi->state = NO_QUEUE; 30528c2ecf20Sopenharmony_ci} 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_cistatic u8 ibmvscsis_fast_fail(struct scsi_info *vscsi, 30558c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd) 30568c2ecf20Sopenharmony_ci{ 30578c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 30588c2ecf20Sopenharmony_ci struct se_cmd *se_cmd = &cmd->se_cmd; 30598c2ecf20Sopenharmony_ci struct srp_cmd *srp = (struct srp_cmd *)iue->sbuf->buf; 30608c2ecf20Sopenharmony_ci struct scsi_sense_hdr sshdr; 30618c2ecf20Sopenharmony_ci u8 rc = se_cmd->scsi_status; 30628c2ecf20Sopenharmony_ci 30638c2ecf20Sopenharmony_ci if (vscsi->fast_fail && (READ_CMD(srp->cdb) || WRITE_CMD(srp->cdb))) 30648c2ecf20Sopenharmony_ci if (scsi_normalize_sense(se_cmd->sense_buffer, 30658c2ecf20Sopenharmony_ci se_cmd->scsi_sense_length, &sshdr)) 30668c2ecf20Sopenharmony_ci if (sshdr.sense_key == HARDWARE_ERROR && 30678c2ecf20Sopenharmony_ci (se_cmd->residual_count == 0 || 30688c2ecf20Sopenharmony_ci se_cmd->residual_count == se_cmd->data_length)) { 30698c2ecf20Sopenharmony_ci rc = NO_SENSE; 30708c2ecf20Sopenharmony_ci cmd->flags |= CMD_FAST_FAIL; 30718c2ecf20Sopenharmony_ci } 30728c2ecf20Sopenharmony_ci 30738c2ecf20Sopenharmony_ci return rc; 30748c2ecf20Sopenharmony_ci} 30758c2ecf20Sopenharmony_ci 30768c2ecf20Sopenharmony_ci/** 30778c2ecf20Sopenharmony_ci * srp_build_response() - Build an SRP response buffer 30788c2ecf20Sopenharmony_ci * @vscsi: Pointer to our adapter structure 30798c2ecf20Sopenharmony_ci * @cmd: Pointer to command for which to send the response 30808c2ecf20Sopenharmony_ci * @len_p: Where to return the length of the IU response sent. This 30818c2ecf20Sopenharmony_ci * is needed to construct the CRQ response. 30828c2ecf20Sopenharmony_ci * 30838c2ecf20Sopenharmony_ci * Build the SRP response buffer and copy it to the client's memory space. 30848c2ecf20Sopenharmony_ci */ 30858c2ecf20Sopenharmony_cistatic long srp_build_response(struct scsi_info *vscsi, 30868c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd, uint *len_p) 30878c2ecf20Sopenharmony_ci{ 30888c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 30898c2ecf20Sopenharmony_ci struct se_cmd *se_cmd = &cmd->se_cmd; 30908c2ecf20Sopenharmony_ci struct srp_rsp *rsp; 30918c2ecf20Sopenharmony_ci uint len; 30928c2ecf20Sopenharmony_ci u32 rsp_code; 30938c2ecf20Sopenharmony_ci char *data; 30948c2ecf20Sopenharmony_ci u32 *tsk_status; 30958c2ecf20Sopenharmony_ci long rc = ADAPT_SUCCESS; 30968c2ecf20Sopenharmony_ci 30978c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 30988c2ecf20Sopenharmony_ci 30998c2ecf20Sopenharmony_ci rsp = &vio_iu(iue)->srp.rsp; 31008c2ecf20Sopenharmony_ci len = sizeof(*rsp); 31018c2ecf20Sopenharmony_ci memset(rsp, 0, len); 31028c2ecf20Sopenharmony_ci data = rsp->data; 31038c2ecf20Sopenharmony_ci 31048c2ecf20Sopenharmony_ci rsp->opcode = SRP_RSP; 31058c2ecf20Sopenharmony_ci 31068c2ecf20Sopenharmony_ci rsp->req_lim_delta = cpu_to_be32(1 + vscsi->credit); 31078c2ecf20Sopenharmony_ci rsp->tag = cmd->rsp.tag; 31088c2ecf20Sopenharmony_ci rsp->flags = 0; 31098c2ecf20Sopenharmony_ci 31108c2ecf20Sopenharmony_ci if (cmd->type == SCSI_CDB) { 31118c2ecf20Sopenharmony_ci rsp->status = ibmvscsis_fast_fail(vscsi, cmd); 31128c2ecf20Sopenharmony_ci if (rsp->status) { 31138c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "build_resp: cmd %p, scsi status %d\n", 31148c2ecf20Sopenharmony_ci cmd, (int)rsp->status); 31158c2ecf20Sopenharmony_ci ibmvscsis_determine_resid(se_cmd, rsp); 31168c2ecf20Sopenharmony_ci if (se_cmd->scsi_sense_length && se_cmd->sense_buffer) { 31178c2ecf20Sopenharmony_ci rsp->sense_data_len = 31188c2ecf20Sopenharmony_ci cpu_to_be32(se_cmd->scsi_sense_length); 31198c2ecf20Sopenharmony_ci rsp->flags |= SRP_RSP_FLAG_SNSVALID; 31208c2ecf20Sopenharmony_ci len += se_cmd->scsi_sense_length; 31218c2ecf20Sopenharmony_ci memcpy(data, se_cmd->sense_buffer, 31228c2ecf20Sopenharmony_ci se_cmd->scsi_sense_length); 31238c2ecf20Sopenharmony_ci } 31248c2ecf20Sopenharmony_ci rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >> 31258c2ecf20Sopenharmony_ci UCSOLNT_RESP_SHIFT; 31268c2ecf20Sopenharmony_ci } else if (cmd->flags & CMD_FAST_FAIL) { 31278c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "build_resp: cmd %p, fast fail\n", 31288c2ecf20Sopenharmony_ci cmd); 31298c2ecf20Sopenharmony_ci rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >> 31308c2ecf20Sopenharmony_ci UCSOLNT_RESP_SHIFT; 31318c2ecf20Sopenharmony_ci } else { 31328c2ecf20Sopenharmony_ci rsp->sol_not = (cmd->rsp.sol_not & SCSOLNT) >> 31338c2ecf20Sopenharmony_ci SCSOLNT_RESP_SHIFT; 31348c2ecf20Sopenharmony_ci } 31358c2ecf20Sopenharmony_ci } else { 31368c2ecf20Sopenharmony_ci /* this is task management */ 31378c2ecf20Sopenharmony_ci rsp->status = 0; 31388c2ecf20Sopenharmony_ci rsp->resp_data_len = cpu_to_be32(4); 31398c2ecf20Sopenharmony_ci rsp->flags |= SRP_RSP_FLAG_RSPVALID; 31408c2ecf20Sopenharmony_ci 31418c2ecf20Sopenharmony_ci switch (se_cmd->se_tmr_req->response) { 31428c2ecf20Sopenharmony_ci case TMR_FUNCTION_COMPLETE: 31438c2ecf20Sopenharmony_ci case TMR_TASK_DOES_NOT_EXIST: 31448c2ecf20Sopenharmony_ci rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_COMPLETE; 31458c2ecf20Sopenharmony_ci rsp->sol_not = (cmd->rsp.sol_not & SCSOLNT) >> 31468c2ecf20Sopenharmony_ci SCSOLNT_RESP_SHIFT; 31478c2ecf20Sopenharmony_ci break; 31488c2ecf20Sopenharmony_ci case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED: 31498c2ecf20Sopenharmony_ci case TMR_LUN_DOES_NOT_EXIST: 31508c2ecf20Sopenharmony_ci rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_NOT_SUPPORTED; 31518c2ecf20Sopenharmony_ci rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >> 31528c2ecf20Sopenharmony_ci UCSOLNT_RESP_SHIFT; 31538c2ecf20Sopenharmony_ci break; 31548c2ecf20Sopenharmony_ci case TMR_FUNCTION_FAILED: 31558c2ecf20Sopenharmony_ci case TMR_FUNCTION_REJECTED: 31568c2ecf20Sopenharmony_ci default: 31578c2ecf20Sopenharmony_ci rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_FAILED; 31588c2ecf20Sopenharmony_ci rsp->sol_not = (cmd->rsp.sol_not & UCSOLNT) >> 31598c2ecf20Sopenharmony_ci UCSOLNT_RESP_SHIFT; 31608c2ecf20Sopenharmony_ci break; 31618c2ecf20Sopenharmony_ci } 31628c2ecf20Sopenharmony_ci 31638c2ecf20Sopenharmony_ci tsk_status = (u32 *)data; 31648c2ecf20Sopenharmony_ci *tsk_status = cpu_to_be32(rsp_code); 31658c2ecf20Sopenharmony_ci data = (char *)(tsk_status + 1); 31668c2ecf20Sopenharmony_ci len += 4; 31678c2ecf20Sopenharmony_ci } 31688c2ecf20Sopenharmony_ci 31698c2ecf20Sopenharmony_ci dma_wmb(); 31708c2ecf20Sopenharmony_ci rc = h_copy_rdma(len, vscsi->dds.window[LOCAL].liobn, iue->sbuf->dma, 31718c2ecf20Sopenharmony_ci vscsi->dds.window[REMOTE].liobn, 31728c2ecf20Sopenharmony_ci be64_to_cpu(iue->remote_token)); 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_ci switch (rc) { 31758c2ecf20Sopenharmony_ci case H_SUCCESS: 31768c2ecf20Sopenharmony_ci vscsi->credit = 0; 31778c2ecf20Sopenharmony_ci *len_p = len; 31788c2ecf20Sopenharmony_ci break; 31798c2ecf20Sopenharmony_ci case H_PERMISSION: 31808c2ecf20Sopenharmony_ci if (connection_broken(vscsi)) 31818c2ecf20Sopenharmony_ci vscsi->flags |= RESPONSE_Q_DOWN | CLIENT_FAILED; 31828c2ecf20Sopenharmony_ci 31838c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "build_response: error copying to client, rc %ld, flags 0x%x, state 0x%hx\n", 31848c2ecf20Sopenharmony_ci rc, vscsi->flags, vscsi->state); 31858c2ecf20Sopenharmony_ci break; 31868c2ecf20Sopenharmony_ci case H_SOURCE_PARM: 31878c2ecf20Sopenharmony_ci case H_DEST_PARM: 31888c2ecf20Sopenharmony_ci default: 31898c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "build_response: error copying to client, rc %ld\n", 31908c2ecf20Sopenharmony_ci rc); 31918c2ecf20Sopenharmony_ci break; 31928c2ecf20Sopenharmony_ci } 31938c2ecf20Sopenharmony_ci 31948c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 31958c2ecf20Sopenharmony_ci 31968c2ecf20Sopenharmony_ci return rc; 31978c2ecf20Sopenharmony_ci} 31988c2ecf20Sopenharmony_ci 31998c2ecf20Sopenharmony_cistatic int ibmvscsis_rdma(struct ibmvscsis_cmd *cmd, struct scatterlist *sg, 32008c2ecf20Sopenharmony_ci int nsg, struct srp_direct_buf *md, int nmd, 32018c2ecf20Sopenharmony_ci enum dma_data_direction dir, unsigned int bytes) 32028c2ecf20Sopenharmony_ci{ 32038c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 32048c2ecf20Sopenharmony_ci struct srp_target *target = iue->target; 32058c2ecf20Sopenharmony_ci struct scsi_info *vscsi = target->ldata; 32068c2ecf20Sopenharmony_ci struct scatterlist *sgp; 32078c2ecf20Sopenharmony_ci dma_addr_t client_ioba, server_ioba; 32088c2ecf20Sopenharmony_ci ulong buf_len; 32098c2ecf20Sopenharmony_ci ulong client_len, server_len; 32108c2ecf20Sopenharmony_ci int md_idx; 32118c2ecf20Sopenharmony_ci long tx_len; 32128c2ecf20Sopenharmony_ci long rc = 0; 32138c2ecf20Sopenharmony_ci 32148c2ecf20Sopenharmony_ci if (bytes == 0) 32158c2ecf20Sopenharmony_ci return 0; 32168c2ecf20Sopenharmony_ci 32178c2ecf20Sopenharmony_ci sgp = sg; 32188c2ecf20Sopenharmony_ci client_len = 0; 32198c2ecf20Sopenharmony_ci server_len = 0; 32208c2ecf20Sopenharmony_ci md_idx = 0; 32218c2ecf20Sopenharmony_ci tx_len = bytes; 32228c2ecf20Sopenharmony_ci 32238c2ecf20Sopenharmony_ci do { 32248c2ecf20Sopenharmony_ci if (client_len == 0) { 32258c2ecf20Sopenharmony_ci if (md_idx >= nmd) { 32268c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "rdma: ran out of client memory descriptors\n"); 32278c2ecf20Sopenharmony_ci rc = -EIO; 32288c2ecf20Sopenharmony_ci break; 32298c2ecf20Sopenharmony_ci } 32308c2ecf20Sopenharmony_ci client_ioba = be64_to_cpu(md[md_idx].va); 32318c2ecf20Sopenharmony_ci client_len = be32_to_cpu(md[md_idx].len); 32328c2ecf20Sopenharmony_ci } 32338c2ecf20Sopenharmony_ci if (server_len == 0) { 32348c2ecf20Sopenharmony_ci if (!sgp) { 32358c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "rdma: ran out of scatter/gather list\n"); 32368c2ecf20Sopenharmony_ci rc = -EIO; 32378c2ecf20Sopenharmony_ci break; 32388c2ecf20Sopenharmony_ci } 32398c2ecf20Sopenharmony_ci server_ioba = sg_dma_address(sgp); 32408c2ecf20Sopenharmony_ci server_len = sg_dma_len(sgp); 32418c2ecf20Sopenharmony_ci } 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci buf_len = tx_len; 32448c2ecf20Sopenharmony_ci 32458c2ecf20Sopenharmony_ci if (buf_len > client_len) 32468c2ecf20Sopenharmony_ci buf_len = client_len; 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_ci if (buf_len > server_len) 32498c2ecf20Sopenharmony_ci buf_len = server_len; 32508c2ecf20Sopenharmony_ci 32518c2ecf20Sopenharmony_ci if (buf_len > max_vdma_size) 32528c2ecf20Sopenharmony_ci buf_len = max_vdma_size; 32538c2ecf20Sopenharmony_ci 32548c2ecf20Sopenharmony_ci if (dir == DMA_TO_DEVICE) { 32558c2ecf20Sopenharmony_ci /* read from client */ 32568c2ecf20Sopenharmony_ci rc = h_copy_rdma(buf_len, 32578c2ecf20Sopenharmony_ci vscsi->dds.window[REMOTE].liobn, 32588c2ecf20Sopenharmony_ci client_ioba, 32598c2ecf20Sopenharmony_ci vscsi->dds.window[LOCAL].liobn, 32608c2ecf20Sopenharmony_ci server_ioba); 32618c2ecf20Sopenharmony_ci } else { 32628c2ecf20Sopenharmony_ci /* The h_copy_rdma will cause phyp, running in another 32638c2ecf20Sopenharmony_ci * partition, to read memory, so we need to make sure 32648c2ecf20Sopenharmony_ci * the data has been written out, hence these syncs. 32658c2ecf20Sopenharmony_ci */ 32668c2ecf20Sopenharmony_ci /* ensure that everything is in memory */ 32678c2ecf20Sopenharmony_ci isync(); 32688c2ecf20Sopenharmony_ci /* ensure that memory has been made visible */ 32698c2ecf20Sopenharmony_ci dma_wmb(); 32708c2ecf20Sopenharmony_ci rc = h_copy_rdma(buf_len, 32718c2ecf20Sopenharmony_ci vscsi->dds.window[LOCAL].liobn, 32728c2ecf20Sopenharmony_ci server_ioba, 32738c2ecf20Sopenharmony_ci vscsi->dds.window[REMOTE].liobn, 32748c2ecf20Sopenharmony_ci client_ioba); 32758c2ecf20Sopenharmony_ci } 32768c2ecf20Sopenharmony_ci switch (rc) { 32778c2ecf20Sopenharmony_ci case H_SUCCESS: 32788c2ecf20Sopenharmony_ci break; 32798c2ecf20Sopenharmony_ci case H_PERMISSION: 32808c2ecf20Sopenharmony_ci case H_SOURCE_PARM: 32818c2ecf20Sopenharmony_ci case H_DEST_PARM: 32828c2ecf20Sopenharmony_ci if (connection_broken(vscsi)) { 32838c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 32848c2ecf20Sopenharmony_ci vscsi->flags |= 32858c2ecf20Sopenharmony_ci (RESPONSE_Q_DOWN | CLIENT_FAILED); 32868c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 32878c2ecf20Sopenharmony_ci } 32888c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "rdma: h_copy_rdma failed, rc %ld\n", 32898c2ecf20Sopenharmony_ci rc); 32908c2ecf20Sopenharmony_ci break; 32918c2ecf20Sopenharmony_ci 32928c2ecf20Sopenharmony_ci default: 32938c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "rdma: unknown error %ld from h_copy_rdma\n", 32948c2ecf20Sopenharmony_ci rc); 32958c2ecf20Sopenharmony_ci break; 32968c2ecf20Sopenharmony_ci } 32978c2ecf20Sopenharmony_ci 32988c2ecf20Sopenharmony_ci if (!rc) { 32998c2ecf20Sopenharmony_ci tx_len -= buf_len; 33008c2ecf20Sopenharmony_ci if (tx_len) { 33018c2ecf20Sopenharmony_ci client_len -= buf_len; 33028c2ecf20Sopenharmony_ci if (client_len == 0) 33038c2ecf20Sopenharmony_ci md_idx++; 33048c2ecf20Sopenharmony_ci else 33058c2ecf20Sopenharmony_ci client_ioba += buf_len; 33068c2ecf20Sopenharmony_ci 33078c2ecf20Sopenharmony_ci server_len -= buf_len; 33088c2ecf20Sopenharmony_ci if (server_len == 0) 33098c2ecf20Sopenharmony_ci sgp = sg_next(sgp); 33108c2ecf20Sopenharmony_ci else 33118c2ecf20Sopenharmony_ci server_ioba += buf_len; 33128c2ecf20Sopenharmony_ci } else { 33138c2ecf20Sopenharmony_ci break; 33148c2ecf20Sopenharmony_ci } 33158c2ecf20Sopenharmony_ci } 33168c2ecf20Sopenharmony_ci } while (!rc); 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci return rc; 33198c2ecf20Sopenharmony_ci} 33208c2ecf20Sopenharmony_ci 33218c2ecf20Sopenharmony_ci/** 33228c2ecf20Sopenharmony_ci * ibmvscsis_handle_crq() - Handle CRQ 33238c2ecf20Sopenharmony_ci * @data: Pointer to our adapter structure 33248c2ecf20Sopenharmony_ci * 33258c2ecf20Sopenharmony_ci * Read the command elements from the command queue and copy the payloads 33268c2ecf20Sopenharmony_ci * associated with the command elements to local memory and execute the 33278c2ecf20Sopenharmony_ci * SRP requests. 33288c2ecf20Sopenharmony_ci * 33298c2ecf20Sopenharmony_ci * Note: this is an edge triggered interrupt. It can not be shared. 33308c2ecf20Sopenharmony_ci */ 33318c2ecf20Sopenharmony_cistatic void ibmvscsis_handle_crq(unsigned long data) 33328c2ecf20Sopenharmony_ci{ 33338c2ecf20Sopenharmony_ci struct scsi_info *vscsi = (struct scsi_info *)data; 33348c2ecf20Sopenharmony_ci struct viosrp_crq *crq; 33358c2ecf20Sopenharmony_ci long rc; 33368c2ecf20Sopenharmony_ci bool ack = true; 33378c2ecf20Sopenharmony_ci volatile u8 valid; 33388c2ecf20Sopenharmony_ci 33398c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "got interrupt\n"); 33428c2ecf20Sopenharmony_ci 33438c2ecf20Sopenharmony_ci /* 33448c2ecf20Sopenharmony_ci * if we are in a path where we are waiting for all pending commands 33458c2ecf20Sopenharmony_ci * to complete because we received a transport event and anything in 33468c2ecf20Sopenharmony_ci * the command queue is for a new connection, do nothing 33478c2ecf20Sopenharmony_ci */ 33488c2ecf20Sopenharmony_ci if (TARGET_STOP(vscsi)) { 33498c2ecf20Sopenharmony_ci vio_enable_interrupts(vscsi->dma_dev); 33508c2ecf20Sopenharmony_ci 33518c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "handle_crq, don't process: flags 0x%x, state 0x%hx\n", 33528c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state); 33538c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 33548c2ecf20Sopenharmony_ci return; 33558c2ecf20Sopenharmony_ci } 33568c2ecf20Sopenharmony_ci 33578c2ecf20Sopenharmony_ci rc = vscsi->flags & SCHEDULE_DISCONNECT; 33588c2ecf20Sopenharmony_ci crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index; 33598c2ecf20Sopenharmony_ci valid = crq->valid; 33608c2ecf20Sopenharmony_ci dma_rmb(); 33618c2ecf20Sopenharmony_ci 33628c2ecf20Sopenharmony_ci while (valid) { 33638c2ecf20Sopenharmony_ci /* 33648c2ecf20Sopenharmony_ci * These are edege triggered interrupts. After dropping out of 33658c2ecf20Sopenharmony_ci * the while loop, the code must check for work since an 33668c2ecf20Sopenharmony_ci * interrupt could be lost, and an elment be left on the queue, 33678c2ecf20Sopenharmony_ci * hence the label. 33688c2ecf20Sopenharmony_ci */ 33698c2ecf20Sopenharmony_cicmd_work: 33708c2ecf20Sopenharmony_ci vscsi->cmd_q.index = 33718c2ecf20Sopenharmony_ci (vscsi->cmd_q.index + 1) & vscsi->cmd_q.mask; 33728c2ecf20Sopenharmony_ci 33738c2ecf20Sopenharmony_ci if (!rc) { 33748c2ecf20Sopenharmony_ci rc = ibmvscsis_parse_command(vscsi, crq); 33758c2ecf20Sopenharmony_ci } else { 33768c2ecf20Sopenharmony_ci if ((uint)crq->valid == VALID_TRANS_EVENT) { 33778c2ecf20Sopenharmony_ci /* 33788c2ecf20Sopenharmony_ci * must service the transport layer events even 33798c2ecf20Sopenharmony_ci * in an error state, dont break out until all 33808c2ecf20Sopenharmony_ci * the consecutive transport events have been 33818c2ecf20Sopenharmony_ci * processed 33828c2ecf20Sopenharmony_ci */ 33838c2ecf20Sopenharmony_ci rc = ibmvscsis_trans_event(vscsi, crq); 33848c2ecf20Sopenharmony_ci } else if (vscsi->flags & TRANS_EVENT) { 33858c2ecf20Sopenharmony_ci /* 33868c2ecf20Sopenharmony_ci * if a transport event has occurred leave 33878c2ecf20Sopenharmony_ci * everything but transport events on the queue 33888c2ecf20Sopenharmony_ci * 33898c2ecf20Sopenharmony_ci * need to decrement the queue index so we can 33908c2ecf20Sopenharmony_ci * look at the element again 33918c2ecf20Sopenharmony_ci */ 33928c2ecf20Sopenharmony_ci if (vscsi->cmd_q.index) 33938c2ecf20Sopenharmony_ci vscsi->cmd_q.index -= 1; 33948c2ecf20Sopenharmony_ci else 33958c2ecf20Sopenharmony_ci /* 33968c2ecf20Sopenharmony_ci * index is at 0 it just wrapped. 33978c2ecf20Sopenharmony_ci * have it index last element in q 33988c2ecf20Sopenharmony_ci */ 33998c2ecf20Sopenharmony_ci vscsi->cmd_q.index = vscsi->cmd_q.mask; 34008c2ecf20Sopenharmony_ci break; 34018c2ecf20Sopenharmony_ci } 34028c2ecf20Sopenharmony_ci } 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci crq->valid = INVALIDATE_CMD_RESP_EL; 34058c2ecf20Sopenharmony_ci 34068c2ecf20Sopenharmony_ci crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index; 34078c2ecf20Sopenharmony_ci valid = crq->valid; 34088c2ecf20Sopenharmony_ci dma_rmb(); 34098c2ecf20Sopenharmony_ci } 34108c2ecf20Sopenharmony_ci 34118c2ecf20Sopenharmony_ci if (!rc) { 34128c2ecf20Sopenharmony_ci if (ack) { 34138c2ecf20Sopenharmony_ci vio_enable_interrupts(vscsi->dma_dev); 34148c2ecf20Sopenharmony_ci ack = false; 34158c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "handle_crq, reenabling interrupts\n"); 34168c2ecf20Sopenharmony_ci } 34178c2ecf20Sopenharmony_ci valid = crq->valid; 34188c2ecf20Sopenharmony_ci dma_rmb(); 34198c2ecf20Sopenharmony_ci if (valid) 34208c2ecf20Sopenharmony_ci goto cmd_work; 34218c2ecf20Sopenharmony_ci } else { 34228c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "handle_crq, error: flags 0x%x, state 0x%hx, crq index 0x%x\n", 34238c2ecf20Sopenharmony_ci vscsi->flags, vscsi->state, vscsi->cmd_q.index); 34248c2ecf20Sopenharmony_ci } 34258c2ecf20Sopenharmony_ci 34268c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Leaving handle_crq: schedule_q empty %d, flags 0x%x, state 0x%hx\n", 34278c2ecf20Sopenharmony_ci (int)list_empty(&vscsi->schedule_q), vscsi->flags, 34288c2ecf20Sopenharmony_ci vscsi->state); 34298c2ecf20Sopenharmony_ci 34308c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 34318c2ecf20Sopenharmony_ci} 34328c2ecf20Sopenharmony_ci 34338c2ecf20Sopenharmony_cistatic int ibmvscsis_probe(struct vio_dev *vdev, 34348c2ecf20Sopenharmony_ci const struct vio_device_id *id) 34358c2ecf20Sopenharmony_ci{ 34368c2ecf20Sopenharmony_ci struct scsi_info *vscsi; 34378c2ecf20Sopenharmony_ci int rc = 0; 34388c2ecf20Sopenharmony_ci long hrc = 0; 34398c2ecf20Sopenharmony_ci char wq_name[24]; 34408c2ecf20Sopenharmony_ci 34418c2ecf20Sopenharmony_ci vscsi = kzalloc(sizeof(*vscsi), GFP_KERNEL); 34428c2ecf20Sopenharmony_ci if (!vscsi) { 34438c2ecf20Sopenharmony_ci rc = -ENOMEM; 34448c2ecf20Sopenharmony_ci dev_err(&vdev->dev, "probe: allocation of adapter failed\n"); 34458c2ecf20Sopenharmony_ci return rc; 34468c2ecf20Sopenharmony_ci } 34478c2ecf20Sopenharmony_ci 34488c2ecf20Sopenharmony_ci vscsi->dma_dev = vdev; 34498c2ecf20Sopenharmony_ci vscsi->dev = vdev->dev; 34508c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vscsi->schedule_q); 34518c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vscsi->waiting_rsp); 34528c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vscsi->active_q); 34538c2ecf20Sopenharmony_ci 34548c2ecf20Sopenharmony_ci snprintf(vscsi->tport.tport_name, IBMVSCSIS_NAMELEN, "%s", 34558c2ecf20Sopenharmony_ci dev_name(&vdev->dev)); 34568c2ecf20Sopenharmony_ci 34578c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "probe tport_name: %s\n", vscsi->tport.tport_name); 34588c2ecf20Sopenharmony_ci 34598c2ecf20Sopenharmony_ci rc = read_dma_window(vscsi); 34608c2ecf20Sopenharmony_ci if (rc) 34618c2ecf20Sopenharmony_ci goto free_adapter; 34628c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "Probe: liobn 0x%x, riobn 0x%x\n", 34638c2ecf20Sopenharmony_ci vscsi->dds.window[LOCAL].liobn, 34648c2ecf20Sopenharmony_ci vscsi->dds.window[REMOTE].liobn); 34658c2ecf20Sopenharmony_ci 34668c2ecf20Sopenharmony_ci snprintf(vscsi->eye, sizeof(vscsi->eye), "VSCSI %s", vdev->name); 34678c2ecf20Sopenharmony_ci 34688c2ecf20Sopenharmony_ci vscsi->dds.unit_id = vdev->unit_address; 34698c2ecf20Sopenharmony_ci strscpy(vscsi->dds.partition_name, partition_name, 34708c2ecf20Sopenharmony_ci sizeof(vscsi->dds.partition_name)); 34718c2ecf20Sopenharmony_ci vscsi->dds.partition_num = partition_number; 34728c2ecf20Sopenharmony_ci 34738c2ecf20Sopenharmony_ci spin_lock_bh(&ibmvscsis_dev_lock); 34748c2ecf20Sopenharmony_ci list_add_tail(&vscsi->list, &ibmvscsis_dev_list); 34758c2ecf20Sopenharmony_ci spin_unlock_bh(&ibmvscsis_dev_lock); 34768c2ecf20Sopenharmony_ci 34778c2ecf20Sopenharmony_ci /* 34788c2ecf20Sopenharmony_ci * TBD: How do we determine # of cmds to request? Do we know how 34798c2ecf20Sopenharmony_ci * many "children" we have? 34808c2ecf20Sopenharmony_ci */ 34818c2ecf20Sopenharmony_ci vscsi->request_limit = INITIAL_SRP_LIMIT; 34828c2ecf20Sopenharmony_ci rc = srp_target_alloc(&vscsi->target, &vdev->dev, vscsi->request_limit, 34838c2ecf20Sopenharmony_ci SRP_MAX_IU_LEN); 34848c2ecf20Sopenharmony_ci if (rc) 34858c2ecf20Sopenharmony_ci goto rem_list; 34868c2ecf20Sopenharmony_ci 34878c2ecf20Sopenharmony_ci vscsi->target.ldata = vscsi; 34888c2ecf20Sopenharmony_ci 34898c2ecf20Sopenharmony_ci rc = ibmvscsis_alloc_cmds(vscsi, vscsi->request_limit); 34908c2ecf20Sopenharmony_ci if (rc) { 34918c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "alloc_cmds failed, rc %d, num %d\n", 34928c2ecf20Sopenharmony_ci rc, vscsi->request_limit); 34938c2ecf20Sopenharmony_ci goto free_target; 34948c2ecf20Sopenharmony_ci } 34958c2ecf20Sopenharmony_ci 34968c2ecf20Sopenharmony_ci /* 34978c2ecf20Sopenharmony_ci * Note: the lock is used in freeing timers, so must initialize 34988c2ecf20Sopenharmony_ci * first so that ordering in case of error is correct. 34998c2ecf20Sopenharmony_ci */ 35008c2ecf20Sopenharmony_ci spin_lock_init(&vscsi->intr_lock); 35018c2ecf20Sopenharmony_ci 35028c2ecf20Sopenharmony_ci rc = ibmvscsis_alloctimer(vscsi); 35038c2ecf20Sopenharmony_ci if (rc) { 35048c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "probe: alloctimer failed, rc %d\n", rc); 35058c2ecf20Sopenharmony_ci goto free_cmds; 35068c2ecf20Sopenharmony_ci } 35078c2ecf20Sopenharmony_ci 35088c2ecf20Sopenharmony_ci rc = ibmvscsis_create_command_q(vscsi, 256); 35098c2ecf20Sopenharmony_ci if (rc) { 35108c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "probe: create_command_q failed, rc %d\n", 35118c2ecf20Sopenharmony_ci rc); 35128c2ecf20Sopenharmony_ci goto free_timer; 35138c2ecf20Sopenharmony_ci } 35148c2ecf20Sopenharmony_ci 35158c2ecf20Sopenharmony_ci vscsi->map_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); 35168c2ecf20Sopenharmony_ci if (!vscsi->map_buf) { 35178c2ecf20Sopenharmony_ci rc = -ENOMEM; 35188c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "probe: allocating cmd buffer failed\n"); 35198c2ecf20Sopenharmony_ci goto destroy_queue; 35208c2ecf20Sopenharmony_ci } 35218c2ecf20Sopenharmony_ci 35228c2ecf20Sopenharmony_ci vscsi->map_ioba = dma_map_single(&vdev->dev, vscsi->map_buf, PAGE_SIZE, 35238c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 35248c2ecf20Sopenharmony_ci if (dma_mapping_error(&vdev->dev, vscsi->map_ioba)) { 35258c2ecf20Sopenharmony_ci rc = -ENOMEM; 35268c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "probe: error mapping command buffer\n"); 35278c2ecf20Sopenharmony_ci goto free_buf; 35288c2ecf20Sopenharmony_ci } 35298c2ecf20Sopenharmony_ci 35308c2ecf20Sopenharmony_ci hrc = h_vioctl(vscsi->dds.unit_id, H_GET_PARTNER_INFO, 35318c2ecf20Sopenharmony_ci (u64)vscsi->map_ioba | ((u64)PAGE_SIZE << 32), 0, 0, 0, 35328c2ecf20Sopenharmony_ci 0); 35338c2ecf20Sopenharmony_ci if (hrc == H_SUCCESS) 35348c2ecf20Sopenharmony_ci vscsi->client_data.partition_number = 35358c2ecf20Sopenharmony_ci be64_to_cpu(*(u64 *)vscsi->map_buf); 35368c2ecf20Sopenharmony_ci /* 35378c2ecf20Sopenharmony_ci * We expect the VIOCTL to fail if we're configured as "any 35388c2ecf20Sopenharmony_ci * client can connect" and the client isn't activated yet. 35398c2ecf20Sopenharmony_ci * We'll make the call again when he sends an init msg. 35408c2ecf20Sopenharmony_ci */ 35418c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "probe hrc %ld, client partition num %d\n", 35428c2ecf20Sopenharmony_ci hrc, vscsi->client_data.partition_number); 35438c2ecf20Sopenharmony_ci 35448c2ecf20Sopenharmony_ci tasklet_init(&vscsi->work_task, ibmvscsis_handle_crq, 35458c2ecf20Sopenharmony_ci (unsigned long)vscsi); 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_ci init_completion(&vscsi->wait_idle); 35488c2ecf20Sopenharmony_ci init_completion(&vscsi->unconfig); 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci snprintf(wq_name, 24, "ibmvscsis%s", dev_name(&vdev->dev)); 35518c2ecf20Sopenharmony_ci vscsi->work_q = create_workqueue(wq_name); 35528c2ecf20Sopenharmony_ci if (!vscsi->work_q) { 35538c2ecf20Sopenharmony_ci rc = -ENOMEM; 35548c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "create_workqueue failed\n"); 35558c2ecf20Sopenharmony_ci goto unmap_buf; 35568c2ecf20Sopenharmony_ci } 35578c2ecf20Sopenharmony_ci 35588c2ecf20Sopenharmony_ci rc = request_irq(vdev->irq, ibmvscsis_interrupt, 0, "ibmvscsis", vscsi); 35598c2ecf20Sopenharmony_ci if (rc) { 35608c2ecf20Sopenharmony_ci rc = -EPERM; 35618c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "probe: request_irq failed, rc %d\n", rc); 35628c2ecf20Sopenharmony_ci goto destroy_WQ; 35638c2ecf20Sopenharmony_ci } 35648c2ecf20Sopenharmony_ci 35658c2ecf20Sopenharmony_ci vscsi->state = WAIT_ENABLED; 35668c2ecf20Sopenharmony_ci 35678c2ecf20Sopenharmony_ci dev_set_drvdata(&vdev->dev, vscsi); 35688c2ecf20Sopenharmony_ci 35698c2ecf20Sopenharmony_ci return 0; 35708c2ecf20Sopenharmony_ci 35718c2ecf20Sopenharmony_cidestroy_WQ: 35728c2ecf20Sopenharmony_ci destroy_workqueue(vscsi->work_q); 35738c2ecf20Sopenharmony_ciunmap_buf: 35748c2ecf20Sopenharmony_ci dma_unmap_single(&vdev->dev, vscsi->map_ioba, PAGE_SIZE, 35758c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 35768c2ecf20Sopenharmony_cifree_buf: 35778c2ecf20Sopenharmony_ci kfree(vscsi->map_buf); 35788c2ecf20Sopenharmony_cidestroy_queue: 35798c2ecf20Sopenharmony_ci tasklet_kill(&vscsi->work_task); 35808c2ecf20Sopenharmony_ci ibmvscsis_unregister_command_q(vscsi); 35818c2ecf20Sopenharmony_ci ibmvscsis_destroy_command_q(vscsi); 35828c2ecf20Sopenharmony_cifree_timer: 35838c2ecf20Sopenharmony_ci ibmvscsis_freetimer(vscsi); 35848c2ecf20Sopenharmony_cifree_cmds: 35858c2ecf20Sopenharmony_ci ibmvscsis_free_cmds(vscsi); 35868c2ecf20Sopenharmony_cifree_target: 35878c2ecf20Sopenharmony_ci srp_target_free(&vscsi->target); 35888c2ecf20Sopenharmony_cirem_list: 35898c2ecf20Sopenharmony_ci spin_lock_bh(&ibmvscsis_dev_lock); 35908c2ecf20Sopenharmony_ci list_del(&vscsi->list); 35918c2ecf20Sopenharmony_ci spin_unlock_bh(&ibmvscsis_dev_lock); 35928c2ecf20Sopenharmony_cifree_adapter: 35938c2ecf20Sopenharmony_ci kfree(vscsi); 35948c2ecf20Sopenharmony_ci 35958c2ecf20Sopenharmony_ci return rc; 35968c2ecf20Sopenharmony_ci} 35978c2ecf20Sopenharmony_ci 35988c2ecf20Sopenharmony_cistatic int ibmvscsis_remove(struct vio_dev *vdev) 35998c2ecf20Sopenharmony_ci{ 36008c2ecf20Sopenharmony_ci struct scsi_info *vscsi = dev_get_drvdata(&vdev->dev); 36018c2ecf20Sopenharmony_ci 36028c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "remove (%s)\n", dev_name(&vscsi->dma_dev->dev)); 36038c2ecf20Sopenharmony_ci 36048c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 36058c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, UNCONFIGURING, 0); 36068c2ecf20Sopenharmony_ci vscsi->flags |= CFG_SLEEPING; 36078c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 36088c2ecf20Sopenharmony_ci wait_for_completion(&vscsi->unconfig); 36098c2ecf20Sopenharmony_ci 36108c2ecf20Sopenharmony_ci vio_disable_interrupts(vdev); 36118c2ecf20Sopenharmony_ci free_irq(vdev->irq, vscsi); 36128c2ecf20Sopenharmony_ci destroy_workqueue(vscsi->work_q); 36138c2ecf20Sopenharmony_ci dma_unmap_single(&vdev->dev, vscsi->map_ioba, PAGE_SIZE, 36148c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 36158c2ecf20Sopenharmony_ci kfree(vscsi->map_buf); 36168c2ecf20Sopenharmony_ci tasklet_kill(&vscsi->work_task); 36178c2ecf20Sopenharmony_ci ibmvscsis_destroy_command_q(vscsi); 36188c2ecf20Sopenharmony_ci ibmvscsis_freetimer(vscsi); 36198c2ecf20Sopenharmony_ci ibmvscsis_free_cmds(vscsi); 36208c2ecf20Sopenharmony_ci srp_target_free(&vscsi->target); 36218c2ecf20Sopenharmony_ci spin_lock_bh(&ibmvscsis_dev_lock); 36228c2ecf20Sopenharmony_ci list_del(&vscsi->list); 36238c2ecf20Sopenharmony_ci spin_unlock_bh(&ibmvscsis_dev_lock); 36248c2ecf20Sopenharmony_ci kfree(vscsi); 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci return 0; 36278c2ecf20Sopenharmony_ci} 36288c2ecf20Sopenharmony_ci 36298c2ecf20Sopenharmony_cistatic ssize_t system_id_show(struct device *dev, 36308c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 36318c2ecf20Sopenharmony_ci{ 36328c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", system_id); 36338c2ecf20Sopenharmony_ci} 36348c2ecf20Sopenharmony_ci 36358c2ecf20Sopenharmony_cistatic ssize_t partition_number_show(struct device *dev, 36368c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 36378c2ecf20Sopenharmony_ci{ 36388c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%x\n", partition_number); 36398c2ecf20Sopenharmony_ci} 36408c2ecf20Sopenharmony_ci 36418c2ecf20Sopenharmony_cistatic ssize_t unit_address_show(struct device *dev, 36428c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 36438c2ecf20Sopenharmony_ci{ 36448c2ecf20Sopenharmony_ci struct scsi_info *vscsi = container_of(dev, struct scsi_info, dev); 36458c2ecf20Sopenharmony_ci 36468c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%x\n", vscsi->dma_dev->unit_address); 36478c2ecf20Sopenharmony_ci} 36488c2ecf20Sopenharmony_ci 36498c2ecf20Sopenharmony_cistatic int ibmvscsis_get_system_info(void) 36508c2ecf20Sopenharmony_ci{ 36518c2ecf20Sopenharmony_ci struct device_node *rootdn, *vdevdn; 36528c2ecf20Sopenharmony_ci const char *id, *model, *name; 36538c2ecf20Sopenharmony_ci const uint *num; 36548c2ecf20Sopenharmony_ci 36558c2ecf20Sopenharmony_ci rootdn = of_find_node_by_path("/"); 36568c2ecf20Sopenharmony_ci if (!rootdn) 36578c2ecf20Sopenharmony_ci return -ENOENT; 36588c2ecf20Sopenharmony_ci 36598c2ecf20Sopenharmony_ci model = of_get_property(rootdn, "model", NULL); 36608c2ecf20Sopenharmony_ci id = of_get_property(rootdn, "system-id", NULL); 36618c2ecf20Sopenharmony_ci if (model && id) 36628c2ecf20Sopenharmony_ci snprintf(system_id, sizeof(system_id), "%s-%s", model, id); 36638c2ecf20Sopenharmony_ci 36648c2ecf20Sopenharmony_ci name = of_get_property(rootdn, "ibm,partition-name", NULL); 36658c2ecf20Sopenharmony_ci if (name) 36668c2ecf20Sopenharmony_ci strncpy(partition_name, name, sizeof(partition_name)); 36678c2ecf20Sopenharmony_ci 36688c2ecf20Sopenharmony_ci num = of_get_property(rootdn, "ibm,partition-no", NULL); 36698c2ecf20Sopenharmony_ci if (num) 36708c2ecf20Sopenharmony_ci partition_number = of_read_number(num, 1); 36718c2ecf20Sopenharmony_ci 36728c2ecf20Sopenharmony_ci of_node_put(rootdn); 36738c2ecf20Sopenharmony_ci 36748c2ecf20Sopenharmony_ci vdevdn = of_find_node_by_path("/vdevice"); 36758c2ecf20Sopenharmony_ci if (vdevdn) { 36768c2ecf20Sopenharmony_ci const uint *mvds; 36778c2ecf20Sopenharmony_ci 36788c2ecf20Sopenharmony_ci mvds = of_get_property(vdevdn, "ibm,max-virtual-dma-size", 36798c2ecf20Sopenharmony_ci NULL); 36808c2ecf20Sopenharmony_ci if (mvds) 36818c2ecf20Sopenharmony_ci max_vdma_size = *mvds; 36828c2ecf20Sopenharmony_ci of_node_put(vdevdn); 36838c2ecf20Sopenharmony_ci } 36848c2ecf20Sopenharmony_ci 36858c2ecf20Sopenharmony_ci return 0; 36868c2ecf20Sopenharmony_ci} 36878c2ecf20Sopenharmony_ci 36888c2ecf20Sopenharmony_cistatic char *ibmvscsis_get_fabric_wwn(struct se_portal_group *se_tpg) 36898c2ecf20Sopenharmony_ci{ 36908c2ecf20Sopenharmony_ci struct ibmvscsis_tport *tport = 36918c2ecf20Sopenharmony_ci container_of(se_tpg, struct ibmvscsis_tport, se_tpg); 36928c2ecf20Sopenharmony_ci 36938c2ecf20Sopenharmony_ci return tport->tport_name; 36948c2ecf20Sopenharmony_ci} 36958c2ecf20Sopenharmony_ci 36968c2ecf20Sopenharmony_cistatic u16 ibmvscsis_get_tag(struct se_portal_group *se_tpg) 36978c2ecf20Sopenharmony_ci{ 36988c2ecf20Sopenharmony_ci struct ibmvscsis_tport *tport = 36998c2ecf20Sopenharmony_ci container_of(se_tpg, struct ibmvscsis_tport, se_tpg); 37008c2ecf20Sopenharmony_ci 37018c2ecf20Sopenharmony_ci return tport->tport_tpgt; 37028c2ecf20Sopenharmony_ci} 37038c2ecf20Sopenharmony_ci 37048c2ecf20Sopenharmony_cistatic u32 ibmvscsis_get_default_depth(struct se_portal_group *se_tpg) 37058c2ecf20Sopenharmony_ci{ 37068c2ecf20Sopenharmony_ci return 1; 37078c2ecf20Sopenharmony_ci} 37088c2ecf20Sopenharmony_ci 37098c2ecf20Sopenharmony_cistatic int ibmvscsis_check_true(struct se_portal_group *se_tpg) 37108c2ecf20Sopenharmony_ci{ 37118c2ecf20Sopenharmony_ci return 1; 37128c2ecf20Sopenharmony_ci} 37138c2ecf20Sopenharmony_ci 37148c2ecf20Sopenharmony_cistatic int ibmvscsis_check_false(struct se_portal_group *se_tpg) 37158c2ecf20Sopenharmony_ci{ 37168c2ecf20Sopenharmony_ci return 0; 37178c2ecf20Sopenharmony_ci} 37188c2ecf20Sopenharmony_ci 37198c2ecf20Sopenharmony_cistatic u32 ibmvscsis_tpg_get_inst_index(struct se_portal_group *se_tpg) 37208c2ecf20Sopenharmony_ci{ 37218c2ecf20Sopenharmony_ci return 1; 37228c2ecf20Sopenharmony_ci} 37238c2ecf20Sopenharmony_ci 37248c2ecf20Sopenharmony_cistatic int ibmvscsis_check_stop_free(struct se_cmd *se_cmd) 37258c2ecf20Sopenharmony_ci{ 37268c2ecf20Sopenharmony_ci return target_put_sess_cmd(se_cmd); 37278c2ecf20Sopenharmony_ci} 37288c2ecf20Sopenharmony_ci 37298c2ecf20Sopenharmony_cistatic void ibmvscsis_release_cmd(struct se_cmd *se_cmd) 37308c2ecf20Sopenharmony_ci{ 37318c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd, 37328c2ecf20Sopenharmony_ci se_cmd); 37338c2ecf20Sopenharmony_ci struct scsi_info *vscsi = cmd->adapter; 37348c2ecf20Sopenharmony_ci 37358c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 37368c2ecf20Sopenharmony_ci /* Remove from active_q */ 37378c2ecf20Sopenharmony_ci list_move_tail(&cmd->list, &vscsi->waiting_rsp); 37388c2ecf20Sopenharmony_ci ibmvscsis_send_messages(vscsi); 37398c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 37408c2ecf20Sopenharmony_ci} 37418c2ecf20Sopenharmony_ci 37428c2ecf20Sopenharmony_cistatic u32 ibmvscsis_sess_get_index(struct se_session *se_sess) 37438c2ecf20Sopenharmony_ci{ 37448c2ecf20Sopenharmony_ci return 0; 37458c2ecf20Sopenharmony_ci} 37468c2ecf20Sopenharmony_ci 37478c2ecf20Sopenharmony_cistatic int ibmvscsis_write_pending(struct se_cmd *se_cmd) 37488c2ecf20Sopenharmony_ci{ 37498c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd, 37508c2ecf20Sopenharmony_ci se_cmd); 37518c2ecf20Sopenharmony_ci struct scsi_info *vscsi = cmd->adapter; 37528c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 37538c2ecf20Sopenharmony_ci int rc; 37548c2ecf20Sopenharmony_ci 37558c2ecf20Sopenharmony_ci /* 37568c2ecf20Sopenharmony_ci * If CLIENT_FAILED OR RESPONSE_Q_DOWN, then just return success 37578c2ecf20Sopenharmony_ci * since LIO can't do anything about it, and we dont want to 37588c2ecf20Sopenharmony_ci * attempt an srp_transfer_data. 37598c2ecf20Sopenharmony_ci */ 37608c2ecf20Sopenharmony_ci if ((vscsi->flags & (CLIENT_FAILED | RESPONSE_Q_DOWN))) { 37618c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "write_pending failed since: %d\n", 37628c2ecf20Sopenharmony_ci vscsi->flags); 37638c2ecf20Sopenharmony_ci return -EIO; 37648c2ecf20Sopenharmony_ci 37658c2ecf20Sopenharmony_ci } 37668c2ecf20Sopenharmony_ci 37678c2ecf20Sopenharmony_ci rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma, 37688c2ecf20Sopenharmony_ci 1, 1); 37698c2ecf20Sopenharmony_ci if (rc) { 37708c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "srp_transfer_data() failed: %d\n", rc); 37718c2ecf20Sopenharmony_ci return -EIO; 37728c2ecf20Sopenharmony_ci } 37738c2ecf20Sopenharmony_ci /* 37748c2ecf20Sopenharmony_ci * We now tell TCM to add this WRITE CDB directly into the TCM storage 37758c2ecf20Sopenharmony_ci * object execution queue. 37768c2ecf20Sopenharmony_ci */ 37778c2ecf20Sopenharmony_ci target_execute_cmd(se_cmd); 37788c2ecf20Sopenharmony_ci return 0; 37798c2ecf20Sopenharmony_ci} 37808c2ecf20Sopenharmony_ci 37818c2ecf20Sopenharmony_cistatic void ibmvscsis_set_default_node_attrs(struct se_node_acl *nacl) 37828c2ecf20Sopenharmony_ci{ 37838c2ecf20Sopenharmony_ci} 37848c2ecf20Sopenharmony_ci 37858c2ecf20Sopenharmony_cistatic int ibmvscsis_get_cmd_state(struct se_cmd *se_cmd) 37868c2ecf20Sopenharmony_ci{ 37878c2ecf20Sopenharmony_ci return 0; 37888c2ecf20Sopenharmony_ci} 37898c2ecf20Sopenharmony_ci 37908c2ecf20Sopenharmony_cistatic int ibmvscsis_queue_data_in(struct se_cmd *se_cmd) 37918c2ecf20Sopenharmony_ci{ 37928c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd, 37938c2ecf20Sopenharmony_ci se_cmd); 37948c2ecf20Sopenharmony_ci struct iu_entry *iue = cmd->iue; 37958c2ecf20Sopenharmony_ci struct scsi_info *vscsi = cmd->adapter; 37968c2ecf20Sopenharmony_ci uint len = 0; 37978c2ecf20Sopenharmony_ci int rc; 37988c2ecf20Sopenharmony_ci 37998c2ecf20Sopenharmony_ci rc = srp_transfer_data(cmd, &vio_iu(iue)->srp.cmd, ibmvscsis_rdma, 1, 38008c2ecf20Sopenharmony_ci 1); 38018c2ecf20Sopenharmony_ci if (rc) { 38028c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "srp_transfer_data failed: %d\n", rc); 38038c2ecf20Sopenharmony_ci se_cmd->scsi_sense_length = 18; 38048c2ecf20Sopenharmony_ci memset(se_cmd->sense_buffer, 0, se_cmd->scsi_sense_length); 38058c2ecf20Sopenharmony_ci /* Logical Unit Communication Time-out asc/ascq = 0x0801 */ 38068c2ecf20Sopenharmony_ci scsi_build_sense_buffer(0, se_cmd->sense_buffer, MEDIUM_ERROR, 38078c2ecf20Sopenharmony_ci 0x08, 0x01); 38088c2ecf20Sopenharmony_ci } 38098c2ecf20Sopenharmony_ci 38108c2ecf20Sopenharmony_ci srp_build_response(vscsi, cmd, &len); 38118c2ecf20Sopenharmony_ci cmd->rsp.format = SRP_FORMAT; 38128c2ecf20Sopenharmony_ci cmd->rsp.len = len; 38138c2ecf20Sopenharmony_ci 38148c2ecf20Sopenharmony_ci return 0; 38158c2ecf20Sopenharmony_ci} 38168c2ecf20Sopenharmony_ci 38178c2ecf20Sopenharmony_cistatic int ibmvscsis_queue_status(struct se_cmd *se_cmd) 38188c2ecf20Sopenharmony_ci{ 38198c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd, 38208c2ecf20Sopenharmony_ci se_cmd); 38218c2ecf20Sopenharmony_ci struct scsi_info *vscsi = cmd->adapter; 38228c2ecf20Sopenharmony_ci uint len; 38238c2ecf20Sopenharmony_ci 38248c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "queue_status %p\n", se_cmd); 38258c2ecf20Sopenharmony_ci 38268c2ecf20Sopenharmony_ci srp_build_response(vscsi, cmd, &len); 38278c2ecf20Sopenharmony_ci cmd->rsp.format = SRP_FORMAT; 38288c2ecf20Sopenharmony_ci cmd->rsp.len = len; 38298c2ecf20Sopenharmony_ci 38308c2ecf20Sopenharmony_ci return 0; 38318c2ecf20Sopenharmony_ci} 38328c2ecf20Sopenharmony_ci 38338c2ecf20Sopenharmony_cistatic void ibmvscsis_queue_tm_rsp(struct se_cmd *se_cmd) 38348c2ecf20Sopenharmony_ci{ 38358c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd, 38368c2ecf20Sopenharmony_ci se_cmd); 38378c2ecf20Sopenharmony_ci struct scsi_info *vscsi = cmd->adapter; 38388c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd_itr; 38398c2ecf20Sopenharmony_ci struct iu_entry *iue = iue = cmd->iue; 38408c2ecf20Sopenharmony_ci struct srp_tsk_mgmt *srp_tsk = &vio_iu(iue)->srp.tsk_mgmt; 38418c2ecf20Sopenharmony_ci u64 tag_to_abort = be64_to_cpu(srp_tsk->task_tag); 38428c2ecf20Sopenharmony_ci uint len; 38438c2ecf20Sopenharmony_ci 38448c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "queue_tm_rsp %p, status %d\n", 38458c2ecf20Sopenharmony_ci se_cmd, (int)se_cmd->se_tmr_req->response); 38468c2ecf20Sopenharmony_ci 38478c2ecf20Sopenharmony_ci if (srp_tsk->tsk_mgmt_func == SRP_TSK_ABORT_TASK && 38488c2ecf20Sopenharmony_ci cmd->se_cmd.se_tmr_req->response == TMR_TASK_DOES_NOT_EXIST) { 38498c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 38508c2ecf20Sopenharmony_ci list_for_each_entry(cmd_itr, &vscsi->active_q, list) { 38518c2ecf20Sopenharmony_ci if (tag_to_abort == cmd_itr->se_cmd.tag) { 38528c2ecf20Sopenharmony_ci cmd_itr->abort_cmd = cmd; 38538c2ecf20Sopenharmony_ci cmd->flags |= DELAY_SEND; 38548c2ecf20Sopenharmony_ci break; 38558c2ecf20Sopenharmony_ci } 38568c2ecf20Sopenharmony_ci } 38578c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 38588c2ecf20Sopenharmony_ci } 38598c2ecf20Sopenharmony_ci 38608c2ecf20Sopenharmony_ci srp_build_response(vscsi, cmd, &len); 38618c2ecf20Sopenharmony_ci cmd->rsp.format = SRP_FORMAT; 38628c2ecf20Sopenharmony_ci cmd->rsp.len = len; 38638c2ecf20Sopenharmony_ci} 38648c2ecf20Sopenharmony_ci 38658c2ecf20Sopenharmony_cistatic void ibmvscsis_aborted_task(struct se_cmd *se_cmd) 38668c2ecf20Sopenharmony_ci{ 38678c2ecf20Sopenharmony_ci struct ibmvscsis_cmd *cmd = container_of(se_cmd, struct ibmvscsis_cmd, 38688c2ecf20Sopenharmony_ci se_cmd); 38698c2ecf20Sopenharmony_ci struct scsi_info *vscsi = cmd->adapter; 38708c2ecf20Sopenharmony_ci 38718c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "ibmvscsis_aborted_task %p task_tag: %llu\n", 38728c2ecf20Sopenharmony_ci se_cmd, se_cmd->tag); 38738c2ecf20Sopenharmony_ci} 38748c2ecf20Sopenharmony_ci 38758c2ecf20Sopenharmony_cistatic struct se_wwn *ibmvscsis_make_tport(struct target_fabric_configfs *tf, 38768c2ecf20Sopenharmony_ci struct config_group *group, 38778c2ecf20Sopenharmony_ci const char *name) 38788c2ecf20Sopenharmony_ci{ 38798c2ecf20Sopenharmony_ci struct ibmvscsis_tport *tport; 38808c2ecf20Sopenharmony_ci struct scsi_info *vscsi; 38818c2ecf20Sopenharmony_ci 38828c2ecf20Sopenharmony_ci tport = ibmvscsis_lookup_port(name); 38838c2ecf20Sopenharmony_ci if (tport) { 38848c2ecf20Sopenharmony_ci vscsi = container_of(tport, struct scsi_info, tport); 38858c2ecf20Sopenharmony_ci tport->tport_proto_id = SCSI_PROTOCOL_SRP; 38868c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "make_tport(%s), pointer:%p, tport_id:%x\n", 38878c2ecf20Sopenharmony_ci name, tport, tport->tport_proto_id); 38888c2ecf20Sopenharmony_ci return &tport->tport_wwn; 38898c2ecf20Sopenharmony_ci } 38908c2ecf20Sopenharmony_ci 38918c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 38928c2ecf20Sopenharmony_ci} 38938c2ecf20Sopenharmony_ci 38948c2ecf20Sopenharmony_cistatic void ibmvscsis_drop_tport(struct se_wwn *wwn) 38958c2ecf20Sopenharmony_ci{ 38968c2ecf20Sopenharmony_ci struct ibmvscsis_tport *tport = container_of(wwn, 38978c2ecf20Sopenharmony_ci struct ibmvscsis_tport, 38988c2ecf20Sopenharmony_ci tport_wwn); 38998c2ecf20Sopenharmony_ci struct scsi_info *vscsi = container_of(tport, struct scsi_info, tport); 39008c2ecf20Sopenharmony_ci 39018c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "drop_tport(%s)\n", 39028c2ecf20Sopenharmony_ci config_item_name(&tport->tport_wwn.wwn_group.cg_item)); 39038c2ecf20Sopenharmony_ci} 39048c2ecf20Sopenharmony_ci 39058c2ecf20Sopenharmony_cistatic struct se_portal_group *ibmvscsis_make_tpg(struct se_wwn *wwn, 39068c2ecf20Sopenharmony_ci const char *name) 39078c2ecf20Sopenharmony_ci{ 39088c2ecf20Sopenharmony_ci struct ibmvscsis_tport *tport = 39098c2ecf20Sopenharmony_ci container_of(wwn, struct ibmvscsis_tport, tport_wwn); 39108c2ecf20Sopenharmony_ci u16 tpgt; 39118c2ecf20Sopenharmony_ci int rc; 39128c2ecf20Sopenharmony_ci 39138c2ecf20Sopenharmony_ci if (strstr(name, "tpgt_") != name) 39148c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 39158c2ecf20Sopenharmony_ci rc = kstrtou16(name + 5, 0, &tpgt); 39168c2ecf20Sopenharmony_ci if (rc) 39178c2ecf20Sopenharmony_ci return ERR_PTR(rc); 39188c2ecf20Sopenharmony_ci tport->tport_tpgt = tpgt; 39198c2ecf20Sopenharmony_ci 39208c2ecf20Sopenharmony_ci tport->releasing = false; 39218c2ecf20Sopenharmony_ci 39228c2ecf20Sopenharmony_ci rc = core_tpg_register(&tport->tport_wwn, &tport->se_tpg, 39238c2ecf20Sopenharmony_ci tport->tport_proto_id); 39248c2ecf20Sopenharmony_ci if (rc) 39258c2ecf20Sopenharmony_ci return ERR_PTR(rc); 39268c2ecf20Sopenharmony_ci 39278c2ecf20Sopenharmony_ci return &tport->se_tpg; 39288c2ecf20Sopenharmony_ci} 39298c2ecf20Sopenharmony_ci 39308c2ecf20Sopenharmony_cistatic void ibmvscsis_drop_tpg(struct se_portal_group *se_tpg) 39318c2ecf20Sopenharmony_ci{ 39328c2ecf20Sopenharmony_ci struct ibmvscsis_tport *tport = container_of(se_tpg, 39338c2ecf20Sopenharmony_ci struct ibmvscsis_tport, 39348c2ecf20Sopenharmony_ci se_tpg); 39358c2ecf20Sopenharmony_ci 39368c2ecf20Sopenharmony_ci tport->releasing = true; 39378c2ecf20Sopenharmony_ci tport->enabled = false; 39388c2ecf20Sopenharmony_ci 39398c2ecf20Sopenharmony_ci /* 39408c2ecf20Sopenharmony_ci * Release the virtual I_T Nexus for this ibmvscsis TPG 39418c2ecf20Sopenharmony_ci */ 39428c2ecf20Sopenharmony_ci ibmvscsis_drop_nexus(tport); 39438c2ecf20Sopenharmony_ci /* 39448c2ecf20Sopenharmony_ci * Deregister the se_tpg from TCM.. 39458c2ecf20Sopenharmony_ci */ 39468c2ecf20Sopenharmony_ci core_tpg_deregister(se_tpg); 39478c2ecf20Sopenharmony_ci} 39488c2ecf20Sopenharmony_ci 39498c2ecf20Sopenharmony_cistatic ssize_t ibmvscsis_wwn_version_show(struct config_item *item, 39508c2ecf20Sopenharmony_ci char *page) 39518c2ecf20Sopenharmony_ci{ 39528c2ecf20Sopenharmony_ci return scnprintf(page, PAGE_SIZE, "%s\n", IBMVSCSIS_VERSION); 39538c2ecf20Sopenharmony_ci} 39548c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(ibmvscsis_wwn_, version); 39558c2ecf20Sopenharmony_ci 39568c2ecf20Sopenharmony_cistatic struct configfs_attribute *ibmvscsis_wwn_attrs[] = { 39578c2ecf20Sopenharmony_ci &ibmvscsis_wwn_attr_version, 39588c2ecf20Sopenharmony_ci NULL, 39598c2ecf20Sopenharmony_ci}; 39608c2ecf20Sopenharmony_ci 39618c2ecf20Sopenharmony_cistatic ssize_t ibmvscsis_tpg_enable_show(struct config_item *item, 39628c2ecf20Sopenharmony_ci char *page) 39638c2ecf20Sopenharmony_ci{ 39648c2ecf20Sopenharmony_ci struct se_portal_group *se_tpg = to_tpg(item); 39658c2ecf20Sopenharmony_ci struct ibmvscsis_tport *tport = container_of(se_tpg, 39668c2ecf20Sopenharmony_ci struct ibmvscsis_tport, 39678c2ecf20Sopenharmony_ci se_tpg); 39688c2ecf20Sopenharmony_ci 39698c2ecf20Sopenharmony_ci return snprintf(page, PAGE_SIZE, "%d\n", (tport->enabled) ? 1 : 0); 39708c2ecf20Sopenharmony_ci} 39718c2ecf20Sopenharmony_ci 39728c2ecf20Sopenharmony_cistatic ssize_t ibmvscsis_tpg_enable_store(struct config_item *item, 39738c2ecf20Sopenharmony_ci const char *page, size_t count) 39748c2ecf20Sopenharmony_ci{ 39758c2ecf20Sopenharmony_ci struct se_portal_group *se_tpg = to_tpg(item); 39768c2ecf20Sopenharmony_ci struct ibmvscsis_tport *tport = container_of(se_tpg, 39778c2ecf20Sopenharmony_ci struct ibmvscsis_tport, 39788c2ecf20Sopenharmony_ci se_tpg); 39798c2ecf20Sopenharmony_ci struct scsi_info *vscsi = container_of(tport, struct scsi_info, tport); 39808c2ecf20Sopenharmony_ci unsigned long tmp; 39818c2ecf20Sopenharmony_ci int rc; 39828c2ecf20Sopenharmony_ci long lrc; 39838c2ecf20Sopenharmony_ci 39848c2ecf20Sopenharmony_ci rc = kstrtoul(page, 0, &tmp); 39858c2ecf20Sopenharmony_ci if (rc < 0) { 39868c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "Unable to extract srpt_tpg_store_enable\n"); 39878c2ecf20Sopenharmony_ci return -EINVAL; 39888c2ecf20Sopenharmony_ci } 39898c2ecf20Sopenharmony_ci 39908c2ecf20Sopenharmony_ci if ((tmp != 0) && (tmp != 1)) { 39918c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "Illegal value for srpt_tpg_store_enable\n"); 39928c2ecf20Sopenharmony_ci return -EINVAL; 39938c2ecf20Sopenharmony_ci } 39948c2ecf20Sopenharmony_ci 39958c2ecf20Sopenharmony_ci if (tmp) { 39968c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 39978c2ecf20Sopenharmony_ci tport->enabled = true; 39988c2ecf20Sopenharmony_ci lrc = ibmvscsis_enable_change_state(vscsi); 39998c2ecf20Sopenharmony_ci if (lrc) 40008c2ecf20Sopenharmony_ci dev_err(&vscsi->dev, "enable_change_state failed, rc %ld state %d\n", 40018c2ecf20Sopenharmony_ci lrc, vscsi->state); 40028c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 40038c2ecf20Sopenharmony_ci } else { 40048c2ecf20Sopenharmony_ci spin_lock_bh(&vscsi->intr_lock); 40058c2ecf20Sopenharmony_ci tport->enabled = false; 40068c2ecf20Sopenharmony_ci /* This simulates the server going down */ 40078c2ecf20Sopenharmony_ci ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0); 40088c2ecf20Sopenharmony_ci spin_unlock_bh(&vscsi->intr_lock); 40098c2ecf20Sopenharmony_ci } 40108c2ecf20Sopenharmony_ci 40118c2ecf20Sopenharmony_ci dev_dbg(&vscsi->dev, "tpg_enable_store, tmp %ld, state %d\n", tmp, 40128c2ecf20Sopenharmony_ci vscsi->state); 40138c2ecf20Sopenharmony_ci 40148c2ecf20Sopenharmony_ci return count; 40158c2ecf20Sopenharmony_ci} 40168c2ecf20Sopenharmony_ciCONFIGFS_ATTR(ibmvscsis_tpg_, enable); 40178c2ecf20Sopenharmony_ci 40188c2ecf20Sopenharmony_cistatic struct configfs_attribute *ibmvscsis_tpg_attrs[] = { 40198c2ecf20Sopenharmony_ci &ibmvscsis_tpg_attr_enable, 40208c2ecf20Sopenharmony_ci NULL, 40218c2ecf20Sopenharmony_ci}; 40228c2ecf20Sopenharmony_ci 40238c2ecf20Sopenharmony_cistatic const struct target_core_fabric_ops ibmvscsis_ops = { 40248c2ecf20Sopenharmony_ci .module = THIS_MODULE, 40258c2ecf20Sopenharmony_ci .fabric_name = "ibmvscsis", 40268c2ecf20Sopenharmony_ci .max_data_sg_nents = MAX_TXU / PAGE_SIZE, 40278c2ecf20Sopenharmony_ci .tpg_get_wwn = ibmvscsis_get_fabric_wwn, 40288c2ecf20Sopenharmony_ci .tpg_get_tag = ibmvscsis_get_tag, 40298c2ecf20Sopenharmony_ci .tpg_get_default_depth = ibmvscsis_get_default_depth, 40308c2ecf20Sopenharmony_ci .tpg_check_demo_mode = ibmvscsis_check_true, 40318c2ecf20Sopenharmony_ci .tpg_check_demo_mode_cache = ibmvscsis_check_true, 40328c2ecf20Sopenharmony_ci .tpg_check_demo_mode_write_protect = ibmvscsis_check_false, 40338c2ecf20Sopenharmony_ci .tpg_check_prod_mode_write_protect = ibmvscsis_check_false, 40348c2ecf20Sopenharmony_ci .tpg_get_inst_index = ibmvscsis_tpg_get_inst_index, 40358c2ecf20Sopenharmony_ci .check_stop_free = ibmvscsis_check_stop_free, 40368c2ecf20Sopenharmony_ci .release_cmd = ibmvscsis_release_cmd, 40378c2ecf20Sopenharmony_ci .sess_get_index = ibmvscsis_sess_get_index, 40388c2ecf20Sopenharmony_ci .write_pending = ibmvscsis_write_pending, 40398c2ecf20Sopenharmony_ci .set_default_node_attributes = ibmvscsis_set_default_node_attrs, 40408c2ecf20Sopenharmony_ci .get_cmd_state = ibmvscsis_get_cmd_state, 40418c2ecf20Sopenharmony_ci .queue_data_in = ibmvscsis_queue_data_in, 40428c2ecf20Sopenharmony_ci .queue_status = ibmvscsis_queue_status, 40438c2ecf20Sopenharmony_ci .queue_tm_rsp = ibmvscsis_queue_tm_rsp, 40448c2ecf20Sopenharmony_ci .aborted_task = ibmvscsis_aborted_task, 40458c2ecf20Sopenharmony_ci /* 40468c2ecf20Sopenharmony_ci * Setup function pointers for logic in target_core_fabric_configfs.c 40478c2ecf20Sopenharmony_ci */ 40488c2ecf20Sopenharmony_ci .fabric_make_wwn = ibmvscsis_make_tport, 40498c2ecf20Sopenharmony_ci .fabric_drop_wwn = ibmvscsis_drop_tport, 40508c2ecf20Sopenharmony_ci .fabric_make_tpg = ibmvscsis_make_tpg, 40518c2ecf20Sopenharmony_ci .fabric_drop_tpg = ibmvscsis_drop_tpg, 40528c2ecf20Sopenharmony_ci 40538c2ecf20Sopenharmony_ci .tfc_wwn_attrs = ibmvscsis_wwn_attrs, 40548c2ecf20Sopenharmony_ci .tfc_tpg_base_attrs = ibmvscsis_tpg_attrs, 40558c2ecf20Sopenharmony_ci}; 40568c2ecf20Sopenharmony_ci 40578c2ecf20Sopenharmony_cistatic void ibmvscsis_dev_release(struct device *dev) {}; 40588c2ecf20Sopenharmony_ci 40598c2ecf20Sopenharmony_cistatic struct device_attribute dev_attr_system_id = 40608c2ecf20Sopenharmony_ci __ATTR(system_id, S_IRUGO, system_id_show, NULL); 40618c2ecf20Sopenharmony_ci 40628c2ecf20Sopenharmony_cistatic struct device_attribute dev_attr_partition_number = 40638c2ecf20Sopenharmony_ci __ATTR(partition_number, S_IRUGO, partition_number_show, NULL); 40648c2ecf20Sopenharmony_ci 40658c2ecf20Sopenharmony_cistatic struct device_attribute dev_attr_unit_address = 40668c2ecf20Sopenharmony_ci __ATTR(unit_address, S_IRUGO, unit_address_show, NULL); 40678c2ecf20Sopenharmony_ci 40688c2ecf20Sopenharmony_cistatic struct attribute *ibmvscsis_dev_attrs[] = { 40698c2ecf20Sopenharmony_ci &dev_attr_system_id.attr, 40708c2ecf20Sopenharmony_ci &dev_attr_partition_number.attr, 40718c2ecf20Sopenharmony_ci &dev_attr_unit_address.attr, 40728c2ecf20Sopenharmony_ci}; 40738c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ibmvscsis_dev); 40748c2ecf20Sopenharmony_ci 40758c2ecf20Sopenharmony_cistatic struct class ibmvscsis_class = { 40768c2ecf20Sopenharmony_ci .name = "ibmvscsis", 40778c2ecf20Sopenharmony_ci .dev_release = ibmvscsis_dev_release, 40788c2ecf20Sopenharmony_ci .dev_groups = ibmvscsis_dev_groups, 40798c2ecf20Sopenharmony_ci}; 40808c2ecf20Sopenharmony_ci 40818c2ecf20Sopenharmony_cistatic const struct vio_device_id ibmvscsis_device_table[] = { 40828c2ecf20Sopenharmony_ci { "v-scsi-host", "IBM,v-scsi-host" }, 40838c2ecf20Sopenharmony_ci { "", "" } 40848c2ecf20Sopenharmony_ci}; 40858c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(vio, ibmvscsis_device_table); 40868c2ecf20Sopenharmony_ci 40878c2ecf20Sopenharmony_cistatic struct vio_driver ibmvscsis_driver = { 40888c2ecf20Sopenharmony_ci .name = "ibmvscsis", 40898c2ecf20Sopenharmony_ci .id_table = ibmvscsis_device_table, 40908c2ecf20Sopenharmony_ci .probe = ibmvscsis_probe, 40918c2ecf20Sopenharmony_ci .remove = ibmvscsis_remove, 40928c2ecf20Sopenharmony_ci}; 40938c2ecf20Sopenharmony_ci 40948c2ecf20Sopenharmony_ci/* 40958c2ecf20Sopenharmony_ci * ibmvscsis_init() - Kernel Module initialization 40968c2ecf20Sopenharmony_ci * 40978c2ecf20Sopenharmony_ci * Note: vio_register_driver() registers callback functions, and at least one 40988c2ecf20Sopenharmony_ci * of those callback functions calls TCM - Linux IO Target Subsystem, thus 40998c2ecf20Sopenharmony_ci * the SCSI Target template must be registered before vio_register_driver() 41008c2ecf20Sopenharmony_ci * is called. 41018c2ecf20Sopenharmony_ci */ 41028c2ecf20Sopenharmony_cistatic int __init ibmvscsis_init(void) 41038c2ecf20Sopenharmony_ci{ 41048c2ecf20Sopenharmony_ci int rc = 0; 41058c2ecf20Sopenharmony_ci 41068c2ecf20Sopenharmony_ci rc = ibmvscsis_get_system_info(); 41078c2ecf20Sopenharmony_ci if (rc) { 41088c2ecf20Sopenharmony_ci pr_err("rc %d from get_system_info\n", rc); 41098c2ecf20Sopenharmony_ci goto out; 41108c2ecf20Sopenharmony_ci } 41118c2ecf20Sopenharmony_ci 41128c2ecf20Sopenharmony_ci rc = class_register(&ibmvscsis_class); 41138c2ecf20Sopenharmony_ci if (rc) { 41148c2ecf20Sopenharmony_ci pr_err("failed class register\n"); 41158c2ecf20Sopenharmony_ci goto out; 41168c2ecf20Sopenharmony_ci } 41178c2ecf20Sopenharmony_ci 41188c2ecf20Sopenharmony_ci rc = target_register_template(&ibmvscsis_ops); 41198c2ecf20Sopenharmony_ci if (rc) { 41208c2ecf20Sopenharmony_ci pr_err("rc %d from target_register_template\n", rc); 41218c2ecf20Sopenharmony_ci goto unregister_class; 41228c2ecf20Sopenharmony_ci } 41238c2ecf20Sopenharmony_ci 41248c2ecf20Sopenharmony_ci rc = vio_register_driver(&ibmvscsis_driver); 41258c2ecf20Sopenharmony_ci if (rc) { 41268c2ecf20Sopenharmony_ci pr_err("rc %d from vio_register_driver\n", rc); 41278c2ecf20Sopenharmony_ci goto unregister_target; 41288c2ecf20Sopenharmony_ci } 41298c2ecf20Sopenharmony_ci 41308c2ecf20Sopenharmony_ci return 0; 41318c2ecf20Sopenharmony_ci 41328c2ecf20Sopenharmony_ciunregister_target: 41338c2ecf20Sopenharmony_ci target_unregister_template(&ibmvscsis_ops); 41348c2ecf20Sopenharmony_ciunregister_class: 41358c2ecf20Sopenharmony_ci class_unregister(&ibmvscsis_class); 41368c2ecf20Sopenharmony_ciout: 41378c2ecf20Sopenharmony_ci return rc; 41388c2ecf20Sopenharmony_ci} 41398c2ecf20Sopenharmony_ci 41408c2ecf20Sopenharmony_cistatic void __exit ibmvscsis_exit(void) 41418c2ecf20Sopenharmony_ci{ 41428c2ecf20Sopenharmony_ci pr_info("Unregister IBM virtual SCSI host driver\n"); 41438c2ecf20Sopenharmony_ci vio_unregister_driver(&ibmvscsis_driver); 41448c2ecf20Sopenharmony_ci target_unregister_template(&ibmvscsis_ops); 41458c2ecf20Sopenharmony_ci class_unregister(&ibmvscsis_class); 41468c2ecf20Sopenharmony_ci} 41478c2ecf20Sopenharmony_ci 41488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IBMVSCSIS fabric driver"); 41498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bryant G. Ly and Michael Cyr"); 41508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 41518c2ecf20Sopenharmony_ciMODULE_VERSION(IBMVSCSIS_VERSION); 41528c2ecf20Sopenharmony_cimodule_init(ibmvscsis_init); 41538c2ecf20Sopenharmony_cimodule_exit(ibmvscsis_exit); 4154