1331766Sken/*- 2331766Sken * Copyright (c) 2017 Broadcom. All rights reserved. 3331766Sken * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries. 4331766Sken * 5331766Sken * Redistribution and use in source and binary forms, with or without 6331766Sken * modification, are permitted provided that the following conditions are met: 7331766Sken * 8331766Sken * 1. Redistributions of source code must retain the above copyright notice, 9331766Sken * this list of conditions and the following disclaimer. 10331766Sken * 11331766Sken * 2. Redistributions in binary form must reproduce the above copyright notice, 12331766Sken * this list of conditions and the following disclaimer in the documentation 13331766Sken * and/or other materials provided with the distribution. 14331766Sken * 15331766Sken * 3. Neither the name of the copyright holder nor the names of its contributors 16331766Sken * may be used to endorse or promote products derived from this software 17331766Sken * without specific prior written permission. 18331766Sken * 19331766Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20331766Sken * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21331766Sken * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22331766Sken * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23331766Sken * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24331766Sken * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25331766Sken * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26331766Sken * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27331766Sken * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28331766Sken * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29331766Sken * POSSIBILITY OF SUCH DAMAGE. 30331766Sken * 31331766Sken * $FreeBSD: stable/11/sys/dev/ocs_fc/ocs_scsi.c 331766 2018-03-30 15:28:25Z ken $ 32331766Sken */ 33331766Sken 34331766Sken/** 35331766Sken * @file 36331766Sken * OCS Linux SCSI API base driver implementation. 37331766Sken */ 38331766Sken 39331766Sken/** 40331766Sken * @defgroup scsi_api_base SCSI Base Target/Initiator 41331766Sken */ 42331766Sken 43331766Sken 44331766Sken#include "ocs.h" 45331766Sken#include "ocs_els.h" 46331766Sken#include "ocs_scsi.h" 47331766Sken#if defined(OCS_ENABLE_VPD_SUPPORT) 48331766Sken#include "ocs_vpd.h" 49331766Sken#endif 50331766Sken#include "ocs_utils.h" 51331766Sken#include "ocs_device.h" 52331766Sken 53331766Sken#define SCSI_IOFMT "[%04x][i:%0*x t:%0*x h:%04x]" 54331766Sken#define SCSI_ITT_SIZE(ocs) ((ocs->ocs_xport == OCS_XPORT_FC) ? 4 : 8) 55331766Sken 56331766Sken#define SCSI_IOFMT_ARGS(io) io->instance_index, SCSI_ITT_SIZE(io->ocs), io->init_task_tag, SCSI_ITT_SIZE(io->ocs), io->tgt_task_tag, io->hw_tag 57331766Sken 58331766Sken#define enable_tsend_auto_resp(ocs) ((ocs->ctrlmask & OCS_CTRLMASK_XPORT_DISABLE_AUTORSP_TSEND) == 0) 59331766Sken#define enable_treceive_auto_resp(ocs) ((ocs->ctrlmask & OCS_CTRLMASK_XPORT_DISABLE_AUTORSP_TRECEIVE) == 0) 60331766Sken 61331766Sken#define scsi_io_printf(io, fmt, ...) ocs_log_info(io->ocs, "[%s]" SCSI_IOFMT fmt, \ 62331766Sken io->node->display_name, SCSI_IOFMT_ARGS(io), ##__VA_ARGS__) 63331766Sken 64331766Sken#define scsi_io_trace(io, fmt, ...) \ 65331766Sken do { \ 66331766Sken if (OCS_LOG_ENABLE_SCSI_TRACE(io->ocs)) \ 67331766Sken scsi_io_printf(io, fmt, ##__VA_ARGS__); \ 68331766Sken } while (0) 69331766Sken 70331766Sken#define scsi_log(ocs, fmt, ...) \ 71331766Sken do { \ 72331766Sken if (OCS_LOG_ENABLE_SCSI_TRACE(ocs)) \ 73331766Sken ocs_log_info(ocs, fmt, ##__VA_ARGS__); \ 74331766Sken } while (0) 75331766Sken 76331766Skenstatic int32_t ocs_target_send_bls_resp(ocs_io_t *io, ocs_scsi_io_cb_t cb, void *arg); 77331766Skenstatic int32_t ocs_scsi_abort_io_cb(struct ocs_hw_io_s *hio, ocs_remote_node_t *rnode, uint32_t len, int32_t status, 78331766Sken uint32_t ext, void *arg); 79331766Sken 80331766Skenstatic void ocs_scsi_io_free_ovfl(ocs_io_t *io); 81331766Skenstatic uint32_t ocs_scsi_count_sgls(ocs_hw_dif_info_t *hw_dif, ocs_scsi_sgl_t *sgl, uint32_t sgl_count); 82331766Skenstatic int ocs_scsi_dif_guard_is_crc(uint8_t direction, ocs_hw_dif_info_t *dif_info); 83331766Skenstatic ocs_scsi_io_status_e ocs_scsi_dif_check_unknown(ocs_io_t *io, uint32_t length, uint32_t check_length, int is_crc); 84331766Skenstatic uint32_t ocs_scsi_dif_check_guard(ocs_hw_dif_info_t *dif_info, ocs_scsi_vaddr_len_t addrlen[], 85331766Sken uint32_t addrlen_count, ocs_dif_t *dif, int is_crc); 86331766Skenstatic uint32_t ocs_scsi_dif_check_app_tag(ocs_t *ocs, ocs_hw_dif_info_t *dif_info, uint16_t exp_app_tag, ocs_dif_t *dif); 87331766Skenstatic uint32_t ocs_scsi_dif_check_ref_tag(ocs_t *ocs, ocs_hw_dif_info_t *dif_info, uint32_t exp_ref_tag, ocs_dif_t *dif); 88331766Skenstatic int32_t ocs_scsi_convert_dif_info(ocs_t *ocs, ocs_scsi_dif_info_t *scsi_dif_info, 89331766Sken ocs_hw_dif_info_t *hw_dif_info); 90331766Skenstatic int32_t ocs_scsi_io_dispatch_hw_io(ocs_io_t *io, ocs_hw_io_t *hio); 91331766Skenstatic int32_t ocs_scsi_io_dispatch_no_hw_io(ocs_io_t *io); 92331766Skenstatic void _ocs_scsi_io_free(void *arg); 93331766Sken 94331766Sken 95331766Sken/** 96331766Sken * @ingroup scsi_api_base 97331766Sken * @brief Returns a big-endian 32-bit value given a pointer. 98331766Sken * 99331766Sken * @param p Pointer to the 32-bit big-endian location. 100331766Sken * 101331766Sken * @return Returns the byte-swapped 32-bit value. 102331766Sken */ 103331766Sken 104331766Skenstatic inline uint32_t 105331766Skenocs_fc_getbe32(void *p) 106331766Sken{ 107331766Sken return ocs_be32toh(*((uint32_t*)p)); 108331766Sken} 109331766Sken 110331766Sken/** 111331766Sken * @ingroup scsi_api_base 112331766Sken * @brief Enable IO allocation. 113331766Sken * 114331766Sken * @par Description 115331766Sken * The SCSI and Transport IO allocation functions are enabled. If the allocation functions 116331766Sken * are not enabled, then calls to ocs_scsi_io_alloc() (and ocs_els_io_alloc() for FC) will 117331766Sken * fail. 118331766Sken * 119331766Sken * @param node Pointer to node object. 120331766Sken * 121331766Sken * @return None. 122331766Sken */ 123331766Skenvoid 124331766Skenocs_scsi_io_alloc_enable(ocs_node_t *node) 125331766Sken{ 126331766Sken ocs_assert(node != NULL); 127331766Sken ocs_lock(&node->active_ios_lock); 128331766Sken node->io_alloc_enabled = TRUE; 129331766Sken ocs_unlock(&node->active_ios_lock); 130331766Sken} 131331766Sken 132331766Sken/** 133331766Sken * @ingroup scsi_api_base 134331766Sken * @brief Disable IO allocation 135331766Sken * 136331766Sken * @par Description 137331766Sken * The SCSI and Transport IO allocation functions are disabled. If the allocation functions 138331766Sken * are not enabled, then calls to ocs_scsi_io_alloc() (and ocs_els_io_alloc() for FC) will 139331766Sken * fail. 140331766Sken * 141331766Sken * @param node Pointer to node object 142331766Sken * 143331766Sken * @return None. 144331766Sken */ 145331766Skenvoid 146331766Skenocs_scsi_io_alloc_disable(ocs_node_t *node) 147331766Sken{ 148331766Sken ocs_assert(node != NULL); 149331766Sken ocs_lock(&node->active_ios_lock); 150331766Sken node->io_alloc_enabled = FALSE; 151331766Sken ocs_unlock(&node->active_ios_lock); 152331766Sken} 153331766Sken 154331766Sken/** 155331766Sken * @ingroup scsi_api_base 156331766Sken * @brief Allocate a SCSI IO context. 157331766Sken * 158331766Sken * @par Description 159331766Sken * A SCSI IO context is allocated and associated with a @c node. This function 160331766Sken * is called by an initiator-client when issuing SCSI commands to remote 161331766Sken * target devices. On completion, ocs_scsi_io_free() is called. 162331766Sken * @n @n 163331766Sken * The returned ocs_io_t structure has an element of type ocs_scsi_ini_io_t named 164331766Sken * "ini_io" that is declared and used by an initiator-client for private information. 165331766Sken * 166331766Sken * @param node Pointer to the associated node structure. 167331766Sken * @param role Role for IO (originator/responder). 168331766Sken * 169331766Sken * @return Returns the pointer to the IO context, or NULL. 170331766Sken * 171331766Sken */ 172331766Sken 173331766Skenocs_io_t * 174331766Skenocs_scsi_io_alloc(ocs_node_t *node, ocs_scsi_io_role_e role) 175331766Sken{ 176331766Sken ocs_t *ocs; 177331766Sken ocs_xport_t *xport; 178331766Sken ocs_io_t *io; 179331766Sken 180331766Sken ocs_assert(node, NULL); 181331766Sken ocs_assert(node->ocs, NULL); 182331766Sken 183331766Sken ocs = node->ocs; 184331766Sken ocs_assert(ocs->xport, NULL); 185331766Sken xport = ocs->xport; 186331766Sken 187331766Sken ocs_lock(&node->active_ios_lock); 188331766Sken 189331766Sken if (!node->io_alloc_enabled) { 190331766Sken ocs_unlock(&node->active_ios_lock); 191331766Sken return NULL; 192331766Sken } 193331766Sken 194331766Sken io = ocs_io_alloc(ocs); 195331766Sken if (io == NULL) { 196331766Sken ocs_atomic_add_return(&xport->io_alloc_failed_count, 1); 197331766Sken ocs_unlock(&node->active_ios_lock); 198331766Sken return NULL; 199331766Sken } 200331766Sken 201331766Sken /* initialize refcount */ 202331766Sken ocs_ref_init(&io->ref, _ocs_scsi_io_free, io); 203331766Sken 204331766Sken if (io->hio != NULL) { 205331766Sken ocs_log_err(node->ocs, "assertion failed: io->hio is not NULL\n"); 206331766Sken ocs_unlock(&node->active_ios_lock); 207331766Sken return NULL; 208331766Sken } 209331766Sken 210331766Sken /* set generic fields */ 211331766Sken io->ocs = ocs; 212331766Sken io->node = node; 213331766Sken 214331766Sken /* set type and name */ 215331766Sken io->io_type = OCS_IO_TYPE_IO; 216331766Sken io->display_name = "scsi_io"; 217331766Sken 218331766Sken switch (role) { 219331766Sken case OCS_SCSI_IO_ROLE_ORIGINATOR: 220331766Sken io->cmd_ini = TRUE; 221331766Sken io->cmd_tgt = FALSE; 222331766Sken break; 223331766Sken case OCS_SCSI_IO_ROLE_RESPONDER: 224331766Sken io->cmd_ini = FALSE; 225331766Sken io->cmd_tgt = TRUE; 226331766Sken break; 227331766Sken } 228331766Sken 229331766Sken /* Add to node's active_ios list */ 230331766Sken ocs_list_add_tail(&node->active_ios, io); 231331766Sken 232331766Sken ocs_unlock(&node->active_ios_lock); 233331766Sken 234331766Sken return io; 235331766Sken} 236331766Sken 237331766Sken/** 238331766Sken * @ingroup scsi_api_base 239331766Sken * @brief Free a SCSI IO context (internal). 240331766Sken * 241331766Sken * @par Description 242331766Sken * The IO context previously allocated using ocs_scsi_io_alloc() 243331766Sken * is freed. This is called from within the transport layer, 244331766Sken * when the reference count goes to zero. 245331766Sken * 246331766Sken * @param arg Pointer to the IO context. 247331766Sken * 248331766Sken * @return None. 249331766Sken */ 250331766Skenstatic void 251331766Sken_ocs_scsi_io_free(void *arg) 252331766Sken{ 253331766Sken ocs_io_t *io = (ocs_io_t *)arg; 254331766Sken ocs_t *ocs = io->ocs; 255331766Sken ocs_node_t *node = io->node; 256331766Sken int send_empty_event; 257331766Sken 258331766Sken ocs_assert(io != NULL); 259331766Sken 260331766Sken scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name); 261331766Sken 262331766Sken ocs_assert(ocs_io_busy(io)); 263331766Sken 264331766Sken ocs_lock(&node->active_ios_lock); 265331766Sken ocs_list_remove(&node->active_ios, io); 266331766Sken send_empty_event = (!node->io_alloc_enabled) && ocs_list_empty(&node->active_ios); 267331766Sken ocs_unlock(&node->active_ios_lock); 268331766Sken 269331766Sken if (send_empty_event) { 270331766Sken ocs_node_post_event(node, OCS_EVT_NODE_ACTIVE_IO_LIST_EMPTY, NULL); 271331766Sken } 272331766Sken 273331766Sken io->node = NULL; 274331766Sken ocs_io_free(ocs, io); 275331766Sken 276331766Sken} 277331766Sken 278331766Sken/** 279331766Sken * @ingroup scsi_api_base 280331766Sken * @brief Free a SCSI IO context. 281331766Sken * 282331766Sken * @par Description 283331766Sken * The IO context previously allocated using ocs_scsi_io_alloc() is freed. 284331766Sken * 285331766Sken * @param io Pointer to the IO context. 286331766Sken * 287331766Sken * @return None. 288331766Sken */ 289331766Skenvoid 290331766Skenocs_scsi_io_free(ocs_io_t *io) 291331766Sken{ 292331766Sken scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name); 293331766Sken ocs_assert(ocs_ref_read_count(&io->ref) > 0); 294331766Sken ocs_ref_put(&io->ref); /* ocs_ref_get(): ocs_scsi_io_alloc() */ 295331766Sken} 296331766Sken 297331766Sken 298331766Sken 299331766Skenstatic int32_t 300331766Skenocs_scsi_send_io(ocs_hw_io_type_e type, ocs_node_t *node, ocs_io_t *io, uint64_t lun, 301331766Sken ocs_scsi_tmf_cmd_e tmf, uint8_t *cdb, uint32_t cdb_len, 302331766Sken ocs_scsi_dif_info_t *dif_info, 303331766Sken ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, uint32_t first_burst, 304331766Sken ocs_scsi_rsp_io_cb_t cb, void *arg); 305331766Sken 306331766Sken/** 307331766Sken * @brief Target response completion callback. 308331766Sken * 309331766Sken * @par Description 310331766Sken * Function is called upon the completion of a target IO request. 311331766Sken * 312331766Sken * @param hio Pointer to the HW IO structure. 313331766Sken * @param rnode Remote node associated with the IO that is completing. 314331766Sken * @param length Length of the response payload. 315331766Sken * @param status Completion status. 316331766Sken * @param ext_status Extended completion status. 317331766Sken * @param app Application-specific data (generally a pointer to the IO context). 318331766Sken * 319331766Sken * @return None. 320331766Sken */ 321331766Sken 322331766Skenstatic void 323331766Skenocs_target_io_cb(ocs_hw_io_t *hio, ocs_remote_node_t *rnode, uint32_t length, 324331766Sken int32_t status, uint32_t ext_status, void *app) 325331766Sken{ 326331766Sken ocs_io_t *io = app; 327331766Sken ocs_t *ocs; 328331766Sken ocs_scsi_io_status_e scsi_status = OCS_SCSI_STATUS_GOOD; 329331766Sken uint16_t additional_length; 330331766Sken uint8_t edir; 331331766Sken uint8_t tdpv; 332331766Sken ocs_hw_dif_info_t *dif_info = &io->hw_dif; 333331766Sken int is_crc; 334331766Sken 335331766Sken ocs_assert(io); 336331766Sken 337331766Sken scsi_io_trace(io, "status x%x ext_status x%x\n", status, ext_status); 338331766Sken 339331766Sken ocs = io->ocs; 340331766Sken ocs_assert(ocs); 341331766Sken 342331766Sken ocs_scsi_io_free_ovfl(io); 343331766Sken 344331766Sken io->transferred += length; 345331766Sken 346331766Sken /* Call target server completion */ 347331766Sken if (io->scsi_tgt_cb) { 348331766Sken ocs_scsi_io_cb_t cb = io->scsi_tgt_cb; 349331766Sken uint32_t flags = 0; 350331766Sken 351331766Sken /* Clear the callback before invoking the callback */ 352331766Sken io->scsi_tgt_cb = NULL; 353331766Sken 354331766Sken /* if status was good, and auto-good-response was set, then callback 355331766Sken * target-server with IO_CMPL_RSP_SENT, otherwise send IO_CMPL 356331766Sken */ 357331766Sken if ((status == 0) && (io->auto_resp)) 358331766Sken flags |= OCS_SCSI_IO_CMPL_RSP_SENT; 359331766Sken else 360331766Sken flags |= OCS_SCSI_IO_CMPL; 361331766Sken 362331766Sken switch (status) { 363331766Sken case SLI4_FC_WCQE_STATUS_SUCCESS: 364331766Sken scsi_status = OCS_SCSI_STATUS_GOOD; 365331766Sken break; 366331766Sken case SLI4_FC_WCQE_STATUS_DI_ERROR: 367331766Sken if (ext_status & SLI4_FC_DI_ERROR_GE) { 368331766Sken scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR; 369331766Sken } else if (ext_status & SLI4_FC_DI_ERROR_AE) { 370331766Sken scsi_status = OCS_SCSI_STATUS_DIF_APP_TAG_ERROR; 371331766Sken } else if (ext_status & SLI4_FC_DI_ERROR_RE) { 372331766Sken scsi_status = OCS_SCSI_STATUS_DIF_REF_TAG_ERROR; 373331766Sken } else { 374331766Sken additional_length = ((ext_status >> 16) & 0xFFFF); 375331766Sken 376331766Sken /* Capture the EDIR and TDPV bits as 0 or 1 for easier printing. */ 377331766Sken edir = !!(ext_status & SLI4_FC_DI_ERROR_EDIR); 378331766Sken tdpv = !!(ext_status & SLI4_FC_DI_ERROR_TDPV); 379331766Sken 380331766Sken is_crc = ocs_scsi_dif_guard_is_crc(edir, dif_info); 381331766Sken 382331766Sken if (edir == 0) { 383331766Sken /* For reads, we have everything in memory. Start checking from beginning. */ 384331766Sken scsi_status = ocs_scsi_dif_check_unknown(io, 0, io->wire_len, is_crc); 385331766Sken } else { 386331766Sken /* For writes, use the additional length to determine where to look for the error. 387331766Sken * The additional_length field is set to 0 if it is not supported. 388331766Sken * The additional length field is valid if: 389331766Sken * . additional_length is not zero 390331766Sken * . Total Data Placed is valid 391331766Sken * . Error Direction is RX (1) 392331766Sken * . Operation is a pass thru (CRC or CKSUM on IN, and CRC or CHKSUM on OUT) (all pass-thru cases except raw) 393331766Sken */ 394331766Sken if ((additional_length != 0) && (tdpv != 0) && 395331766Sken (dif_info->dif == SLI4_DIF_PASS_THROUGH) && (dif_info->dif_oper != OCS_HW_SGE_DIF_OP_IN_RAW_OUT_RAW) ) { 396331766Sken scsi_status = ocs_scsi_dif_check_unknown(io, length, additional_length, is_crc); 397331766Sken } else { 398331766Sken /* If we can't do additional checking, then fall-back to guard error */ 399331766Sken scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR; 400331766Sken } 401331766Sken } 402331766Sken } 403331766Sken break; 404331766Sken case SLI4_FC_WCQE_STATUS_LOCAL_REJECT: 405331766Sken switch (ext_status) { 406331766Sken case SLI4_FC_LOCAL_REJECT_INVALID_RELOFFSET: 407331766Sken case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED: 408331766Sken scsi_status = OCS_SCSI_STATUS_ABORTED; 409331766Sken break; 410331766Sken case SLI4_FC_LOCAL_REJECT_INVALID_RPI: 411331766Sken scsi_status = OCS_SCSI_STATUS_NEXUS_LOST; 412331766Sken break; 413331766Sken case SLI4_FC_LOCAL_REJECT_NO_XRI: 414331766Sken scsi_status = OCS_SCSI_STATUS_NO_IO; 415331766Sken break; 416331766Sken default: 417331766Sken /* TODO: we have seen 0x0d (TX_DMA_FAILED error) */ 418331766Sken scsi_status = OCS_SCSI_STATUS_ERROR; 419331766Sken break; 420331766Sken } 421331766Sken break; 422331766Sken 423331766Sken case SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT: 424331766Sken /* target IO timed out */ 425331766Sken scsi_status = OCS_SCSI_STATUS_TIMEDOUT_AND_ABORTED; 426331766Sken break; 427331766Sken 428331766Sken case SLI4_FC_WCQE_STATUS_SHUTDOWN: 429331766Sken /* Target IO cancelled by HW */ 430331766Sken scsi_status = OCS_SCSI_STATUS_SHUTDOWN; 431331766Sken break; 432331766Sken 433331766Sken default: 434331766Sken scsi_status = OCS_SCSI_STATUS_ERROR; 435331766Sken break; 436331766Sken } 437331766Sken 438331766Sken cb(io, scsi_status, flags, io->scsi_tgt_cb_arg); 439331766Sken 440331766Sken } 441331766Sken ocs_scsi_check_pending(ocs); 442331766Sken} 443331766Sken 444331766Sken/** 445331766Sken * @brief Determine if an IO is using CRC for DIF guard format. 446331766Sken * 447331766Sken * @param direction IO direction: 1 for write, 0 for read. 448331766Sken * @param dif_info Pointer to HW DIF info data. 449331766Sken * 450331766Sken * @return Returns TRUE if using CRC, FALSE if not. 451331766Sken */ 452331766Skenstatic int 453331766Skenocs_scsi_dif_guard_is_crc(uint8_t direction, ocs_hw_dif_info_t *dif_info) 454331766Sken{ 455331766Sken int is_crc; 456331766Sken 457331766Sken if (direction) { 458331766Sken /* For writes, check if operation is "OUT_CRC" or not */ 459331766Sken switch(dif_info->dif_oper) { 460331766Sken case OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CRC: 461331766Sken case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC: 462331766Sken case OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CRC: 463331766Sken is_crc = TRUE; 464331766Sken break; 465331766Sken default: 466331766Sken is_crc = FALSE; 467331766Sken break; 468331766Sken } 469331766Sken } else { 470331766Sken /* For reads, check if operation is "IN_CRC" or not */ 471331766Sken switch(dif_info->dif_oper) { 472331766Sken case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_NODIF: 473331766Sken case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC: 474331766Sken case OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CHKSUM: 475331766Sken is_crc = TRUE; 476331766Sken break; 477331766Sken default: 478331766Sken is_crc = FALSE; 479331766Sken break; 480331766Sken } 481331766Sken } 482331766Sken 483331766Sken return is_crc; 484331766Sken} 485331766Sken 486331766Sken/** 487331766Sken * @brief Check a block and DIF data, computing the appropriate SCSI status 488331766Sken * 489331766Sken * @par Description 490331766Sken * This function is used to check blocks and DIF when given an unknown DIF 491331766Sken * status using the following logic: 492331766Sken * 493331766Sken * Given the address of the last good block, and a length of bytes that includes 494331766Sken * the block with the DIF error, find the bad block. If a block is found with an 495331766Sken * app_tag or ref_tag error, then return the appropriate error. No block is expected 496331766Sken * to have a block guard error since hardware "fixes" the crc. So if no block in the 497331766Sken * range of blocks has an error, then it is presumed to be a BLOCK GUARD error. 498331766Sken * 499331766Sken * @param io Pointer to the IO object. 500331766Sken * @param length Length of bytes covering the good blocks. 501331766Sken * @param check_length Length of bytes that covers the bad block. 502331766Sken * @param is_crc True if guard is using CRC format. 503331766Sken * 504331766Sken * @return Returns SCSI status. 505331766Sken */ 506331766Sken 507331766Skenstatic ocs_scsi_io_status_e 508331766Skenocs_scsi_dif_check_unknown(ocs_io_t *io, uint32_t length, uint32_t check_length, int is_crc) 509331766Sken{ 510331766Sken uint32_t i; 511331766Sken ocs_t *ocs = io->ocs; 512331766Sken ocs_hw_dif_info_t *dif_info = &io->hw_dif; 513331766Sken ocs_scsi_io_status_e scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR; 514331766Sken uint32_t blocksize; /* data block size */ 515331766Sken uint64_t first_check_block; /* first block following total data placed */ 516331766Sken uint64_t last_check_block; /* last block to check */ 517331766Sken uint32_t check_count; /* count of blocks to check */ 518331766Sken ocs_scsi_vaddr_len_t addrlen[4]; /* address-length pairs returned from target */ 519331766Sken int32_t addrlen_count; /* count of address-length pairs */ 520331766Sken ocs_dif_t *dif; /* pointer to DIF block returned from target */ 521331766Sken ocs_scsi_dif_info_t scsi_dif_info = io->scsi_dif_info; 522331766Sken 523331766Sken blocksize = ocs_hw_dif_mem_blocksize(&io->hw_dif, TRUE); 524331766Sken first_check_block = length / blocksize; 525331766Sken last_check_block = ((length + check_length) / blocksize); 526331766Sken check_count = last_check_block - first_check_block; 527331766Sken 528331766Sken ocs_log_debug(ocs, "blocksize %d first check_block %" PRId64 " last_check_block %" PRId64 " check_count %d\n", 529331766Sken blocksize, first_check_block, last_check_block, check_count); 530331766Sken 531331766Sken for (i = first_check_block; i < last_check_block; i++) { 532331766Sken addrlen_count = ocs_scsi_get_block_vaddr(io, (scsi_dif_info.lba + i), addrlen, ARRAY_SIZE(addrlen), (void**) &dif); 533331766Sken if (addrlen_count < 0) { 534331766Sken ocs_log_test(ocs, "ocs_scsi_get_block_vaddr() failed: %d\n", addrlen_count); 535331766Sken scsi_status = OCS_SCSI_STATUS_DIF_UNKNOWN_ERROR; 536331766Sken break; 537331766Sken } 538331766Sken 539331766Sken if (! ocs_scsi_dif_check_guard(dif_info, addrlen, addrlen_count, dif, is_crc)) { 540331766Sken ocs_log_debug(ocs, "block guard check error, lba %" PRId64 "\n", scsi_dif_info.lba + i); 541331766Sken scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR; 542331766Sken break; 543331766Sken } 544331766Sken if (! ocs_scsi_dif_check_app_tag(ocs, dif_info, scsi_dif_info.app_tag, dif)) { 545331766Sken ocs_log_debug(ocs, "app tag check error, lba %" PRId64 "\n", scsi_dif_info.lba + i); 546331766Sken scsi_status = OCS_SCSI_STATUS_DIF_APP_TAG_ERROR; 547331766Sken break; 548331766Sken } 549331766Sken if (! ocs_scsi_dif_check_ref_tag(ocs, dif_info, (scsi_dif_info.ref_tag + i), dif)) { 550331766Sken ocs_log_debug(ocs, "ref tag check error, lba %" PRId64 "\n", scsi_dif_info.lba + i); 551331766Sken scsi_status = OCS_SCSI_STATUS_DIF_REF_TAG_ERROR; 552331766Sken break; 553331766Sken } 554331766Sken 555331766Sken } 556331766Sken return scsi_status; 557331766Sken} 558331766Sken 559331766Sken/** 560331766Sken * @brief Check the block guard of block data 561331766Sken * 562331766Sken * @par Description 563331766Sken * Using the dif_info for the transfer, check the block guard value. 564331766Sken * 565331766Sken * @param dif_info Pointer to HW DIF info data. 566331766Sken * @param addrlen Array of address length pairs. 567331766Sken * @param addrlen_count Number of entries in the addrlen[] array. 568331766Sken * @param dif Pointer to the DIF data block being checked. 569331766Sken * @param is_crc True if guard is using CRC format. 570331766Sken * 571331766Sken * @return Returns TRUE if block guard check is ok. 572331766Sken */ 573331766Skenstatic uint32_t 574331766Skenocs_scsi_dif_check_guard(ocs_hw_dif_info_t *dif_info, ocs_scsi_vaddr_len_t addrlen[], uint32_t addrlen_count, 575331766Sken ocs_dif_t *dif, int is_crc) 576331766Sken{ 577331766Sken uint16_t crc = dif_info->dif_seed; 578331766Sken uint32_t i; 579331766Sken uint16_t checksum; 580331766Sken 581331766Sken if ((dif == NULL) || !dif_info->check_guard) { 582331766Sken return TRUE; 583331766Sken } 584331766Sken 585331766Sken if (is_crc) { 586331766Sken for (i = 0; i < addrlen_count; i++) { 587331766Sken crc = ocs_scsi_dif_calc_crc(addrlen[i].vaddr, addrlen[i].length, crc); 588331766Sken } 589331766Sken return (crc == ocs_be16toh(dif->crc)); 590331766Sken } else { 591331766Sken checksum = ocs_scsi_dif_calc_checksum(addrlen, addrlen_count); 592331766Sken 593331766Sken return (checksum == dif->crc); 594331766Sken } 595331766Sken} 596331766Sken 597331766Sken/** 598331766Sken * @brief Check the app tag of dif data 599331766Sken * 600331766Sken * @par Description 601331766Sken * Using the dif_info for the transfer, check the app tag. 602331766Sken * 603331766Sken * @param ocs Pointer to the ocs structure for logging. 604331766Sken * @param dif_info Pointer to HW DIF info data. 605331766Sken * @param exp_app_tag The value the app tag is expected to be. 606331766Sken * @param dif Pointer to the DIF data block being checked. 607331766Sken * 608331766Sken * @return Returns TRUE if app tag check is ok. 609331766Sken */ 610331766Skenstatic uint32_t 611331766Skenocs_scsi_dif_check_app_tag(ocs_t *ocs, ocs_hw_dif_info_t *dif_info, uint16_t exp_app_tag, ocs_dif_t *dif) 612331766Sken{ 613331766Sken if ((dif == NULL) || !dif_info->check_app_tag) { 614331766Sken return TRUE; 615331766Sken } 616331766Sken 617331766Sken ocs_log_debug(ocs, "expected app tag 0x%x, actual 0x%x\n", 618331766Sken exp_app_tag, ocs_be16toh(dif->app_tag)); 619331766Sken 620331766Sken return (exp_app_tag == ocs_be16toh(dif->app_tag)); 621331766Sken} 622331766Sken 623331766Sken/** 624331766Sken * @brief Check the ref tag of dif data 625331766Sken * 626331766Sken * @par Description 627331766Sken * Using the dif_info for the transfer, check the app tag. 628331766Sken * 629331766Sken * @param ocs Pointer to the ocs structure for logging. 630331766Sken * @param dif_info Pointer to HW DIF info data. 631331766Sken * @param exp_ref_tag The value the ref tag is expected to be. 632331766Sken * @param dif Pointer to the DIF data block being checked. 633331766Sken * 634331766Sken * @return Returns TRUE if ref tag check is ok. 635331766Sken */ 636331766Skenstatic uint32_t 637331766Skenocs_scsi_dif_check_ref_tag(ocs_t *ocs, ocs_hw_dif_info_t *dif_info, uint32_t exp_ref_tag, ocs_dif_t *dif) 638331766Sken{ 639331766Sken if ((dif == NULL) || !dif_info->check_ref_tag) { 640331766Sken return TRUE; 641331766Sken } 642331766Sken 643331766Sken if (exp_ref_tag != ocs_be32toh(dif->ref_tag)) { 644331766Sken ocs_log_debug(ocs, "expected ref tag 0x%x, actual 0x%x\n", 645331766Sken exp_ref_tag, ocs_be32toh(dif->ref_tag)); 646331766Sken return FALSE; 647331766Sken } else { 648331766Sken return TRUE; 649331766Sken } 650331766Sken} 651331766Sken 652331766Sken/** 653331766Sken * @brief Return count of SGE's required for request 654331766Sken * 655331766Sken * @par Description 656331766Sken * An accurate count of SGEs is computed and returned. 657331766Sken * 658331766Sken * @param hw_dif Pointer to HW dif information. 659331766Sken * @param sgl Pointer to SGL from back end. 660331766Sken * @param sgl_count Count of SGEs in SGL. 661331766Sken * 662331766Sken * @return Count of SGEs. 663331766Sken */ 664331766Skenstatic uint32_t 665331766Skenocs_scsi_count_sgls(ocs_hw_dif_info_t *hw_dif, ocs_scsi_sgl_t *sgl, uint32_t sgl_count) 666331766Sken{ 667331766Sken uint32_t count = 0; 668331766Sken uint32_t i; 669331766Sken 670331766Sken /* Convert DIF Information */ 671331766Sken if (hw_dif->dif_oper != OCS_HW_DIF_OPER_DISABLED) { 672331766Sken 673331766Sken /* If we're not DIF separate, then emit a seed SGE */ 674331766Sken if (!hw_dif->dif_separate) { 675331766Sken count++; 676331766Sken } 677331766Sken 678331766Sken for (i = 0; i < sgl_count; i++) { 679331766Sken /* If DIF is enabled, and DIF is separate, then append a SEED then DIF SGE */ 680331766Sken if (hw_dif->dif_separate) { 681331766Sken count += 2; 682331766Sken } 683331766Sken 684331766Sken count++; 685331766Sken } 686331766Sken } else { 687331766Sken count = sgl_count; 688331766Sken } 689331766Sken return count; 690331766Sken} 691331766Sken 692331766Skenstatic int32_t 693331766Skenocs_scsi_build_sgls(ocs_hw_t *hw, ocs_hw_io_t *hio, ocs_hw_dif_info_t *hw_dif, ocs_scsi_sgl_t *sgl, uint32_t sgl_count, ocs_hw_io_type_e type) 694331766Sken{ 695331766Sken int32_t rc; 696331766Sken uint32_t i; 697331766Sken ocs_t *ocs = hw->os; 698331766Sken uint32_t blocksize = 0; 699331766Sken uint32_t blockcount; 700331766Sken 701331766Sken ocs_assert(hio, -1); 702331766Sken 703331766Sken /* Initialize HW SGL */ 704331766Sken rc = ocs_hw_io_init_sges(hw, hio, type); 705331766Sken if (rc) { 706331766Sken ocs_log_err(ocs, "ocs_hw_io_init_sges failed: %d\n", rc); 707331766Sken return -1; 708331766Sken } 709331766Sken 710331766Sken /* Convert DIF Information */ 711331766Sken if (hw_dif->dif_oper != OCS_HW_DIF_OPER_DISABLED) { 712331766Sken 713331766Sken /* If we're not DIF separate, then emit a seed SGE */ 714331766Sken if (!hw_dif->dif_separate) { 715331766Sken rc = ocs_hw_io_add_seed_sge(hw, hio, hw_dif); 716331766Sken if (rc) { 717331766Sken return rc; 718331766Sken } 719331766Sken } 720331766Sken 721331766Sken /* if we are doing DIF separate, then figure out the block size so that we 722331766Sken * can update the ref tag in the DIF seed SGE. Also verify that the 723331766Sken * the sgl lengths are all multiples of the blocksize 724331766Sken */ 725331766Sken if (hw_dif->dif_separate) { 726331766Sken switch(hw_dif->blk_size) { 727331766Sken case OCS_HW_DIF_BK_SIZE_512: blocksize = 512; break; 728331766Sken case OCS_HW_DIF_BK_SIZE_1024: blocksize = 1024; break; 729331766Sken case OCS_HW_DIF_BK_SIZE_2048: blocksize = 2048; break; 730331766Sken case OCS_HW_DIF_BK_SIZE_4096: blocksize = 4096; break; 731331766Sken case OCS_HW_DIF_BK_SIZE_520: blocksize = 520; break; 732331766Sken case OCS_HW_DIF_BK_SIZE_4104: blocksize = 4104; break; 733331766Sken default: 734331766Sken ocs_log_test(hw->os, "Inavlid hw_dif blocksize %d\n", hw_dif->blk_size); 735331766Sken return -1; 736331766Sken } 737331766Sken for (i = 0; i < sgl_count; i++) { 738331766Sken if ((sgl[i].len % blocksize) != 0) { 739331766Sken ocs_log_test(hw->os, "sgl[%d] len of %ld is not multiple of blocksize\n", 740331766Sken i, sgl[i].len); 741331766Sken return -1; 742331766Sken } 743331766Sken } 744331766Sken } 745331766Sken 746331766Sken for (i = 0; i < sgl_count; i++) { 747331766Sken ocs_assert(sgl[i].addr, -1); 748331766Sken ocs_assert(sgl[i].len, -1); 749331766Sken 750331766Sken /* If DIF is enabled, and DIF is separate, then append a SEED then DIF SGE */ 751331766Sken if (hw_dif->dif_separate) { 752331766Sken rc = ocs_hw_io_add_seed_sge(hw, hio, hw_dif); 753331766Sken if (rc) { 754331766Sken return rc; 755331766Sken } 756331766Sken rc = ocs_hw_io_add_dif_sge(hw, hio, sgl[i].dif_addr); 757331766Sken if (rc) { 758331766Sken return rc; 759331766Sken } 760331766Sken /* Update the ref_tag for the next DIF seed SGE */ 761331766Sken blockcount = sgl[i].len / blocksize; 762331766Sken if (hw_dif->dif_oper == OCS_HW_DIF_OPER_INSERT) { 763331766Sken hw_dif->ref_tag_repl += blockcount; 764331766Sken } else { 765331766Sken hw_dif->ref_tag_cmp += blockcount; 766331766Sken } 767331766Sken } 768331766Sken 769331766Sken /* Add data SGE */ 770331766Sken rc = ocs_hw_io_add_sge(hw, hio, sgl[i].addr, sgl[i].len); 771331766Sken if (rc) { 772331766Sken ocs_log_err(ocs, "ocs_hw_io_add_sge failed: count=%d rc=%d\n", 773331766Sken sgl_count, rc); 774331766Sken return rc; 775331766Sken } 776331766Sken } 777331766Sken } else { 778331766Sken for (i = 0; i < sgl_count; i++) { 779331766Sken ocs_assert(sgl[i].addr, -1); 780331766Sken ocs_assert(sgl[i].len, -1); 781331766Sken 782331766Sken /* Add data SGE */ 783331766Sken rc = ocs_hw_io_add_sge(hw, hio, sgl[i].addr, sgl[i].len); 784331766Sken if (rc) { 785331766Sken ocs_log_err(ocs, "ocs_hw_io_add_sge failed: count=%d rc=%d\n", 786331766Sken sgl_count, rc); 787331766Sken return rc; 788331766Sken } 789331766Sken 790331766Sken } 791331766Sken } 792331766Sken return 0; 793331766Sken} 794331766Sken 795331766Sken/** 796331766Sken * @ingroup scsi_api_base 797331766Sken * @brief Convert SCSI API T10 DIF information into the FC HW format. 798331766Sken * 799331766Sken * @param ocs Pointer to the ocs structure for logging. 800331766Sken * @param scsi_dif_info Pointer to the SCSI API T10 DIF fields. 801331766Sken * @param hw_dif_info Pointer to the FC HW API T10 DIF fields. 802331766Sken * 803331766Sken * @return Returns 0 on success, or a negative error code value on failure. 804331766Sken */ 805331766Sken 806331766Skenstatic int32_t 807331766Skenocs_scsi_convert_dif_info(ocs_t *ocs, ocs_scsi_dif_info_t *scsi_dif_info, ocs_hw_dif_info_t *hw_dif_info) 808331766Sken{ 809331766Sken uint32_t dif_seed; 810331766Sken ocs_memset(hw_dif_info, 0, sizeof(ocs_hw_dif_info_t)); 811331766Sken 812331766Sken if (scsi_dif_info == NULL) { 813331766Sken hw_dif_info->dif_oper = OCS_HW_DIF_OPER_DISABLED; 814331766Sken hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_NA; 815331766Sken return 0; 816331766Sken } 817331766Sken 818331766Sken /* Convert the DIF operation */ 819331766Sken switch(scsi_dif_info->dif_oper) { 820331766Sken case OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC: 821331766Sken hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CRC; 822331766Sken hw_dif_info->dif = SLI4_DIF_INSERT; 823331766Sken break; 824331766Sken case OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF: 825331766Sken hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CRC_OUT_NODIF; 826331766Sken hw_dif_info->dif = SLI4_DIF_STRIP; 827331766Sken break; 828331766Sken case OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM: 829331766Sken hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_NODIF_OUT_CHKSUM; 830331766Sken hw_dif_info->dif = SLI4_DIF_INSERT; 831331766Sken break; 832331766Sken case OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF: 833331766Sken hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_NODIF; 834331766Sken hw_dif_info->dif = SLI4_DIF_STRIP; 835331766Sken break; 836331766Sken case OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC: 837331766Sken hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CRC; 838331766Sken hw_dif_info->dif = SLI4_DIF_PASS_THROUGH; 839331766Sken break; 840331766Sken case OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM: 841331766Sken hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CHKSUM; 842331766Sken hw_dif_info->dif = SLI4_DIF_PASS_THROUGH; 843331766Sken break; 844331766Sken case OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM: 845331766Sken hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CRC_OUT_CHKSUM; 846331766Sken hw_dif_info->dif = SLI4_DIF_PASS_THROUGH; 847331766Sken break; 848331766Sken case OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC: 849331766Sken hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_CHKSUM_OUT_CRC; 850331766Sken hw_dif_info->dif = SLI4_DIF_PASS_THROUGH; 851331766Sken break; 852331766Sken case OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW: 853331766Sken hw_dif_info->dif_oper = OCS_HW_SGE_DIF_OP_IN_RAW_OUT_RAW; 854331766Sken hw_dif_info->dif = SLI4_DIF_PASS_THROUGH; 855331766Sken break; 856331766Sken default: 857331766Sken ocs_log_test(ocs, "unhandled SCSI DIF operation %d\n", 858331766Sken scsi_dif_info->dif_oper); 859331766Sken return -1; 860331766Sken } 861331766Sken 862331766Sken switch(scsi_dif_info->blk_size) { 863331766Sken case OCS_SCSI_DIF_BK_SIZE_512: 864331766Sken hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_512; 865331766Sken break; 866331766Sken case OCS_SCSI_DIF_BK_SIZE_1024: 867331766Sken hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_1024; 868331766Sken break; 869331766Sken case OCS_SCSI_DIF_BK_SIZE_2048: 870331766Sken hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_2048; 871331766Sken break; 872331766Sken case OCS_SCSI_DIF_BK_SIZE_4096: 873331766Sken hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_4096; 874331766Sken break; 875331766Sken case OCS_SCSI_DIF_BK_SIZE_520: 876331766Sken hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_520; 877331766Sken break; 878331766Sken case OCS_SCSI_DIF_BK_SIZE_4104: 879331766Sken hw_dif_info->blk_size = OCS_HW_DIF_BK_SIZE_4104; 880331766Sken break; 881331766Sken default: 882331766Sken ocs_log_test(ocs, "unhandled SCSI DIF block size %d\n", 883331766Sken scsi_dif_info->blk_size); 884331766Sken return -1; 885331766Sken } 886331766Sken 887331766Sken /* If the operation is an INSERT the tags provided are the ones that should be 888331766Sken * inserted, otherwise they're the ones to be checked against. */ 889331766Sken if (hw_dif_info->dif == SLI4_DIF_INSERT ) { 890331766Sken hw_dif_info->ref_tag_repl = scsi_dif_info->ref_tag; 891331766Sken hw_dif_info->app_tag_repl = scsi_dif_info->app_tag; 892331766Sken } else { 893331766Sken hw_dif_info->ref_tag_cmp = scsi_dif_info->ref_tag; 894331766Sken hw_dif_info->app_tag_cmp = scsi_dif_info->app_tag; 895331766Sken } 896331766Sken 897331766Sken hw_dif_info->check_ref_tag = scsi_dif_info->check_ref_tag; 898331766Sken hw_dif_info->check_app_tag = scsi_dif_info->check_app_tag; 899331766Sken hw_dif_info->check_guard = scsi_dif_info->check_guard; 900331766Sken hw_dif_info->auto_incr_ref_tag = 1; 901331766Sken hw_dif_info->dif_separate = scsi_dif_info->dif_separate; 902331766Sken hw_dif_info->disable_app_ffff = scsi_dif_info->disable_app_ffff; 903331766Sken hw_dif_info->disable_app_ref_ffff = scsi_dif_info->disable_app_ref_ffff; 904331766Sken 905331766Sken ocs_hw_get(&ocs->hw, OCS_HW_DIF_SEED, &dif_seed); 906331766Sken hw_dif_info->dif_seed = dif_seed; 907331766Sken 908331766Sken return 0; 909331766Sken} 910331766Sken 911331766Sken/** 912331766Sken * @ingroup scsi_api_base 913331766Sken * @brief This function logs the SGLs for an IO. 914331766Sken * 915331766Sken * @param io Pointer to the IO context. 916331766Sken */ 917331766Skenstatic void ocs_log_sgl(ocs_io_t *io) 918331766Sken{ 919331766Sken ocs_hw_io_t *hio = io->hio; 920331766Sken sli4_sge_t *data = NULL; 921331766Sken uint32_t *dword = NULL; 922331766Sken uint32_t i; 923331766Sken uint32_t n_sge; 924331766Sken 925331766Sken scsi_io_trace(io, "def_sgl at 0x%x 0x%08x\n", 926331766Sken ocs_addr32_hi(hio->def_sgl.phys), 927331766Sken ocs_addr32_lo(hio->def_sgl.phys)); 928331766Sken n_sge = (hio->sgl == &hio->def_sgl ? hio->n_sge : hio->def_sgl_count); 929331766Sken for (i = 0, data = hio->def_sgl.virt; i < n_sge; i++, data++) { 930331766Sken dword = (uint32_t*)data; 931331766Sken 932331766Sken scsi_io_trace(io, "SGL %2d 0x%08x 0x%08x 0x%08x 0x%08x\n", 933331766Sken i, dword[0], dword[1], dword[2], dword[3]); 934331766Sken 935331766Sken if (dword[2] & (1U << 31)) { 936331766Sken break; 937331766Sken } 938331766Sken } 939331766Sken 940331766Sken if (hio->ovfl_sgl != NULL && 941331766Sken hio->sgl == hio->ovfl_sgl) { 942331766Sken scsi_io_trace(io, "Overflow at 0x%x 0x%08x\n", 943331766Sken ocs_addr32_hi(hio->ovfl_sgl->phys), 944331766Sken ocs_addr32_lo(hio->ovfl_sgl->phys)); 945331766Sken for (i = 0, data = hio->ovfl_sgl->virt; i < hio->n_sge; i++, data++) { 946331766Sken dword = (uint32_t*)data; 947331766Sken 948331766Sken scsi_io_trace(io, "SGL %2d 0x%08x 0x%08x 0x%08x 0x%08x\n", 949331766Sken i, dword[0], dword[1], dword[2], dword[3]); 950331766Sken if (dword[2] & (1U << 31)) { 951331766Sken break; 952331766Sken } 953331766Sken } 954331766Sken } 955331766Sken 956331766Sken} 957331766Sken 958331766Sken 959331766Sken/** 960331766Sken * @brief Check pending error asynchronous callback function. 961331766Sken * 962331766Sken * @par Description 963331766Sken * Invoke the HW callback function for a given IO. This function is called 964331766Sken * from the NOP mailbox completion context. 965331766Sken * 966331766Sken * @param hw Pointer to HW object. 967331766Sken * @param status Completion status. 968331766Sken * @param mqe Mailbox completion queue entry. 969331766Sken * @param arg General purpose argument. 970331766Sken * 971331766Sken * @return Returns 0. 972331766Sken */ 973331766Skenstatic int32_t 974331766Skenocs_scsi_check_pending_async_cb(ocs_hw_t *hw, int32_t status, uint8_t *mqe, void *arg) 975331766Sken{ 976331766Sken ocs_io_t *io = arg; 977331766Sken 978331766Sken if (io != NULL) { 979331766Sken if (io->hw_cb != NULL) { 980331766Sken ocs_hw_done_t cb = io->hw_cb; 981331766Sken 982331766Sken io->hw_cb = NULL; 983331766Sken cb(io->hio, NULL, 0, SLI4_FC_WCQE_STATUS_DISPATCH_ERROR, 0, io); 984331766Sken } 985331766Sken } 986331766Sken return 0; 987331766Sken} 988331766Sken 989331766Sken/** 990331766Sken * @brief Check for pending IOs to dispatch. 991331766Sken * 992331766Sken * @par Description 993331766Sken * If there are IOs on the pending list, and a HW IO is available, then 994331766Sken * dispatch the IOs. 995331766Sken * 996331766Sken * @param ocs Pointer to the OCS structure. 997331766Sken * 998331766Sken * @return None. 999331766Sken */ 1000331766Sken 1001331766Skenvoid 1002331766Skenocs_scsi_check_pending(ocs_t *ocs) 1003331766Sken{ 1004331766Sken ocs_xport_t *xport = ocs->xport; 1005331766Sken ocs_io_t *io; 1006331766Sken ocs_hw_io_t *hio; 1007331766Sken int32_t status; 1008331766Sken int count = 0; 1009331766Sken int dispatch; 1010331766Sken 1011331766Sken /* Guard against recursion */ 1012331766Sken if (ocs_atomic_add_return(&xport->io_pending_recursing, 1)) { 1013331766Sken /* This function is already running. Decrement and return. */ 1014331766Sken ocs_atomic_sub_return(&xport->io_pending_recursing, 1); 1015331766Sken return; 1016331766Sken } 1017331766Sken 1018331766Sken do { 1019331766Sken ocs_lock(&xport->io_pending_lock); 1020331766Sken status = 0; 1021331766Sken hio = NULL; 1022331766Sken io = ocs_list_remove_head(&xport->io_pending_list); 1023331766Sken if (io != NULL) { 1024331766Sken if (io->io_type == OCS_IO_TYPE_ABORT) { 1025331766Sken hio = NULL; 1026331766Sken } else { 1027331766Sken hio = ocs_hw_io_alloc(&ocs->hw); 1028331766Sken if (hio == NULL) { 1029331766Sken /* 1030331766Sken * No HW IO available. 1031331766Sken * Put IO back on the front of pending list 1032331766Sken */ 1033331766Sken ocs_list_add_head(&xport->io_pending_list, io); 1034331766Sken io = NULL; 1035331766Sken } else { 1036331766Sken hio->eq = io->hw_priv; 1037331766Sken } 1038331766Sken } 1039331766Sken } 1040331766Sken /* Must drop the lock before dispatching the IO */ 1041331766Sken ocs_unlock(&xport->io_pending_lock); 1042331766Sken 1043331766Sken if (io != NULL) { 1044331766Sken count++; 1045331766Sken 1046331766Sken /* 1047331766Sken * We pulled an IO off the pending list, 1048331766Sken * and either got an HW IO or don't need one 1049331766Sken */ 1050331766Sken ocs_atomic_sub_return(&xport->io_pending_count, 1); 1051331766Sken if (hio == NULL) { 1052331766Sken status = ocs_scsi_io_dispatch_no_hw_io(io); 1053331766Sken } else { 1054331766Sken status = ocs_scsi_io_dispatch_hw_io(io, hio); 1055331766Sken } 1056331766Sken if (status) { 1057331766Sken /* 1058331766Sken * Invoke the HW callback, but do so in the separate execution context, 1059331766Sken * provided by the NOP mailbox completion processing context by using 1060331766Sken * ocs_hw_async_call() 1061331766Sken */ 1062331766Sken if (ocs_hw_async_call(&ocs->hw, ocs_scsi_check_pending_async_cb, io)) { 1063331766Sken ocs_log_test(ocs, "call to ocs_hw_async_call() failed\n"); 1064331766Sken } 1065331766Sken } 1066331766Sken } 1067331766Sken } while (io != NULL); 1068331766Sken 1069331766Sken 1070331766Sken /* 1071331766Sken * If nothing was removed from the list, 1072331766Sken * we might be in a case where we need to abort an 1073331766Sken * active IO and the abort is on the pending list. 1074331766Sken * Look for an abort we can dispatch. 1075331766Sken */ 1076331766Sken if (count == 0 ) { 1077331766Sken dispatch = 0; 1078331766Sken 1079331766Sken ocs_lock(&xport->io_pending_lock); 1080331766Sken ocs_list_foreach(&xport->io_pending_list, io) { 1081331766Sken if (io->io_type == OCS_IO_TYPE_ABORT) { 1082331766Sken if (io->io_to_abort->hio != NULL) { 1083331766Sken /* This IO has a HW IO, so it is active. Dispatch the abort. */ 1084331766Sken dispatch = 1; 1085331766Sken } else { 1086331766Sken /* Leave this abort on the pending list and keep looking */ 1087331766Sken dispatch = 0; 1088331766Sken } 1089331766Sken } 1090331766Sken if (dispatch) { 1091331766Sken ocs_list_remove(&xport->io_pending_list, io); 1092331766Sken ocs_atomic_sub_return(&xport->io_pending_count, 1); 1093331766Sken break; 1094331766Sken } 1095331766Sken } 1096331766Sken ocs_unlock(&xport->io_pending_lock); 1097331766Sken 1098331766Sken if (dispatch) { 1099331766Sken status = ocs_scsi_io_dispatch_no_hw_io(io); 1100331766Sken if (status) { 1101331766Sken if (ocs_hw_async_call(&ocs->hw, ocs_scsi_check_pending_async_cb, io)) { 1102331766Sken ocs_log_test(ocs, "call to ocs_hw_async_call() failed\n"); 1103331766Sken } 1104331766Sken } 1105331766Sken } 1106331766Sken } 1107331766Sken 1108331766Sken ocs_atomic_sub_return(&xport->io_pending_recursing, 1); 1109331766Sken return; 1110331766Sken} 1111331766Sken 1112331766Sken/** 1113331766Sken * @brief Attempt to dispatch a non-abort IO 1114331766Sken * 1115331766Sken * @par Description 1116331766Sken * An IO is dispatched: 1117331766Sken * - if the pending list is not empty, add IO to pending list 1118331766Sken * and call a function to process the pending list. 1119331766Sken * - if pending list is empty, try to allocate a HW IO. If none 1120331766Sken * is available, place this IO at the tail of the pending IO 1121331766Sken * list. 1122331766Sken * - if HW IO is available, attach this IO to the HW IO and 1123331766Sken * submit it. 1124331766Sken * 1125331766Sken * @param io Pointer to IO structure. 1126331766Sken * @param cb Callback function. 1127331766Sken * 1128331766Sken * @return Returns 0 on success, a negative error code value on failure. 1129331766Sken */ 1130331766Sken 1131331766Skenint32_t 1132331766Skenocs_scsi_io_dispatch(ocs_io_t *io, void *cb) 1133331766Sken{ 1134331766Sken ocs_hw_io_t *hio; 1135331766Sken ocs_t *ocs = io->ocs; 1136331766Sken ocs_xport_t *xport = ocs->xport; 1137331766Sken 1138331766Sken ocs_assert(io->cmd_tgt || io->cmd_ini, -1); 1139331766Sken ocs_assert((io->io_type != OCS_IO_TYPE_ABORT), -1); 1140331766Sken io->hw_cb = cb; 1141331766Sken 1142331766Sken /* 1143331766Sken * if this IO already has a HW IO, then this is either not the first phase of 1144331766Sken * the IO. Send it to the HW. 1145331766Sken */ 1146331766Sken if (io->hio != NULL) { 1147331766Sken return ocs_scsi_io_dispatch_hw_io(io, io->hio); 1148331766Sken } 1149331766Sken 1150331766Sken /* 1151331766Sken * We don't already have a HW IO associated with the IO. First check 1152331766Sken * the pending list. If not empty, add IO to the tail and process the 1153331766Sken * pending list. 1154331766Sken */ 1155331766Sken ocs_lock(&xport->io_pending_lock); 1156331766Sken if (!ocs_list_empty(&xport->io_pending_list)) { 1157331766Sken /* 1158331766Sken * If this is a low latency request, the put at the front of the IO pending 1159331766Sken * queue, otherwise put it at the end of the queue. 1160331766Sken */ 1161331766Sken if (io->low_latency) { 1162331766Sken ocs_list_add_head(&xport->io_pending_list, io); 1163331766Sken } else { 1164331766Sken ocs_list_add_tail(&xport->io_pending_list, io); 1165331766Sken } 1166331766Sken ocs_unlock(&xport->io_pending_lock); 1167331766Sken ocs_atomic_add_return(&xport->io_pending_count, 1); 1168331766Sken ocs_atomic_add_return(&xport->io_total_pending, 1); 1169331766Sken 1170331766Sken /* process pending list */ 1171331766Sken ocs_scsi_check_pending(ocs); 1172331766Sken return 0; 1173331766Sken } 1174331766Sken ocs_unlock(&xport->io_pending_lock); 1175331766Sken 1176331766Sken /* 1177331766Sken * We don't have a HW IO associated with the IO and there's nothing 1178331766Sken * on the pending list. Attempt to allocate a HW IO and dispatch it. 1179331766Sken */ 1180331766Sken hio = ocs_hw_io_alloc(&io->ocs->hw); 1181331766Sken if (hio == NULL) { 1182331766Sken 1183331766Sken /* Couldn't get a HW IO. Save this IO on the pending list */ 1184331766Sken ocs_lock(&xport->io_pending_lock); 1185331766Sken ocs_list_add_tail(&xport->io_pending_list, io); 1186331766Sken ocs_unlock(&xport->io_pending_lock); 1187331766Sken 1188331766Sken ocs_atomic_add_return(&xport->io_total_pending, 1); 1189331766Sken ocs_atomic_add_return(&xport->io_pending_count, 1); 1190331766Sken return 0; 1191331766Sken } 1192331766Sken 1193331766Sken /* We successfully allocated a HW IO; dispatch to HW */ 1194331766Sken return ocs_scsi_io_dispatch_hw_io(io, hio); 1195331766Sken} 1196331766Sken 1197331766Sken/** 1198331766Sken * @brief Attempt to dispatch an Abort IO. 1199331766Sken * 1200331766Sken * @par Description 1201331766Sken * An Abort IO is dispatched: 1202331766Sken * - if the pending list is not empty, add IO to pending list 1203331766Sken * and call a function to process the pending list. 1204331766Sken * - if pending list is empty, send abort to the HW. 1205331766Sken * 1206331766Sken * @param io Pointer to IO structure. 1207331766Sken * @param cb Callback function. 1208331766Sken * 1209331766Sken * @return Returns 0 on success, a negative error code value on failure. 1210331766Sken */ 1211331766Sken 1212331766Skenint32_t 1213331766Skenocs_scsi_io_dispatch_abort(ocs_io_t *io, void *cb) 1214331766Sken{ 1215331766Sken ocs_t *ocs = io->ocs; 1216331766Sken ocs_xport_t *xport = ocs->xport; 1217331766Sken 1218331766Sken ocs_assert((io->io_type == OCS_IO_TYPE_ABORT), -1); 1219331766Sken io->hw_cb = cb; 1220331766Sken 1221331766Sken /* 1222331766Sken * For aborts, we don't need a HW IO, but we still want to pass through 1223331766Sken * the pending list to preserve ordering. Thus, if the pending list is 1224331766Sken * not empty, add this abort to the pending list and process the pending list. 1225331766Sken */ 1226331766Sken ocs_lock(&xport->io_pending_lock); 1227331766Sken if (!ocs_list_empty(&xport->io_pending_list)) { 1228331766Sken ocs_list_add_tail(&xport->io_pending_list, io); 1229331766Sken ocs_unlock(&xport->io_pending_lock); 1230331766Sken ocs_atomic_add_return(&xport->io_pending_count, 1); 1231331766Sken ocs_atomic_add_return(&xport->io_total_pending, 1); 1232331766Sken 1233331766Sken /* process pending list */ 1234331766Sken ocs_scsi_check_pending(ocs); 1235331766Sken return 0; 1236331766Sken } 1237331766Sken ocs_unlock(&xport->io_pending_lock); 1238331766Sken 1239331766Sken /* nothing on pending list, dispatch abort */ 1240331766Sken return ocs_scsi_io_dispatch_no_hw_io(io); 1241331766Sken 1242331766Sken} 1243331766Sken 1244331766Sken/** 1245331766Sken * @brief Dispatch IO 1246331766Sken * 1247331766Sken * @par Description 1248331766Sken * An IO and its associated HW IO is dispatched to the HW. 1249331766Sken * 1250331766Sken * @param io Pointer to IO structure. 1251331766Sken * @param hio Pointer to HW IO structure from which IO will be 1252331766Sken * dispatched. 1253331766Sken * 1254331766Sken * @return Returns 0 on success, a negative error code value on failure. 1255331766Sken */ 1256331766Sken 1257331766Skenstatic int32_t 1258331766Skenocs_scsi_io_dispatch_hw_io(ocs_io_t *io, ocs_hw_io_t *hio) 1259331766Sken{ 1260331766Sken int32_t rc; 1261331766Sken ocs_t *ocs = io->ocs; 1262331766Sken 1263331766Sken /* Got a HW IO; update ini/tgt_task_tag with HW IO info and dispatch */ 1264331766Sken io->hio = hio; 1265331766Sken if (io->cmd_tgt) { 1266331766Sken io->tgt_task_tag = hio->indicator; 1267331766Sken } else if (io->cmd_ini) { 1268331766Sken io->init_task_tag = hio->indicator; 1269331766Sken } 1270331766Sken io->hw_tag = hio->reqtag; 1271331766Sken 1272331766Sken hio->eq = io->hw_priv; 1273331766Sken 1274331766Sken /* Copy WQ steering */ 1275331766Sken switch(io->wq_steering) { 1276331766Sken case OCS_SCSI_WQ_STEERING_CLASS >> OCS_SCSI_WQ_STEERING_SHIFT: 1277331766Sken hio->wq_steering = OCS_HW_WQ_STEERING_CLASS; 1278331766Sken break; 1279331766Sken case OCS_SCSI_WQ_STEERING_REQUEST >> OCS_SCSI_WQ_STEERING_SHIFT: 1280331766Sken hio->wq_steering = OCS_HW_WQ_STEERING_REQUEST; 1281331766Sken break; 1282331766Sken case OCS_SCSI_WQ_STEERING_CPU >> OCS_SCSI_WQ_STEERING_SHIFT: 1283331766Sken hio->wq_steering = OCS_HW_WQ_STEERING_CPU; 1284331766Sken break; 1285331766Sken } 1286331766Sken 1287331766Sken 1288331766Sken switch (io->io_type) { 1289331766Sken case OCS_IO_TYPE_IO: { 1290331766Sken uint32_t max_sgl; 1291331766Sken uint32_t total_count; 1292331766Sken uint32_t host_allocated; 1293331766Sken 1294331766Sken ocs_hw_get(&ocs->hw, OCS_HW_N_SGL, &max_sgl); 1295331766Sken ocs_hw_get(&ocs->hw, OCS_HW_SGL_CHAINING_HOST_ALLOCATED, &host_allocated); 1296331766Sken 1297331766Sken /* 1298331766Sken * If the requested SGL is larger than the default size, then we can allocate 1299331766Sken * an overflow SGL. 1300331766Sken */ 1301331766Sken total_count = ocs_scsi_count_sgls(&io->hw_dif, io->sgl, io->sgl_count); 1302331766Sken 1303331766Sken /* 1304331766Sken * Lancer requires us to allocate the chained memory area, but 1305331766Sken * Skyhawk must use the SGL list associated with another XRI. 1306331766Sken */ 1307331766Sken if (host_allocated && total_count > max_sgl) { 1308331766Sken /* Compute count needed, the number extra plus 1 for the link sge */ 1309331766Sken uint32_t count = total_count - max_sgl + 1; 1310331766Sken rc = ocs_dma_alloc(ocs, &io->ovfl_sgl, count*sizeof(sli4_sge_t), 64); 1311331766Sken if (rc) { 1312331766Sken ocs_log_err(ocs, "ocs_dma_alloc overflow sgl failed\n"); 1313331766Sken break; 1314331766Sken } 1315331766Sken rc = ocs_hw_io_register_sgl(&ocs->hw, io->hio, &io->ovfl_sgl, count); 1316331766Sken if (rc) { 1317331766Sken ocs_scsi_io_free_ovfl(io); 1318331766Sken ocs_log_err(ocs, "ocs_hw_io_register_sgl() failed\n"); 1319331766Sken break; 1320331766Sken } 1321331766Sken /* EVT: update chained_io_count */ 1322331766Sken io->node->chained_io_count++; 1323331766Sken } 1324331766Sken 1325331766Sken rc = ocs_scsi_build_sgls(&ocs->hw, io->hio, &io->hw_dif, io->sgl, io->sgl_count, io->hio_type); 1326331766Sken if (rc) { 1327331766Sken ocs_scsi_io_free_ovfl(io); 1328331766Sken break; 1329331766Sken } 1330331766Sken 1331331766Sken if (OCS_LOG_ENABLE_SCSI_TRACE(ocs)) { 1332331766Sken ocs_log_sgl(io); 1333331766Sken } 1334331766Sken 1335331766Sken if (io->app_id) { 1336331766Sken io->iparam.fcp_tgt.app_id = io->app_id; 1337331766Sken } 1338331766Sken 1339331766Sken rc = ocs_hw_io_send(&io->ocs->hw, io->hio_type, io->hio, io->wire_len, &io->iparam, &io->node->rnode, 1340331766Sken io->hw_cb, io); 1341331766Sken break; 1342331766Sken } 1343331766Sken case OCS_IO_TYPE_ELS: 1344331766Sken case OCS_IO_TYPE_CT: { 1345331766Sken rc = ocs_hw_srrs_send(&ocs->hw, io->hio_type, io->hio, 1346331766Sken &io->els_req, io->wire_len, 1347331766Sken &io->els_rsp, &io->node->rnode, &io->iparam, 1348331766Sken io->hw_cb, io); 1349331766Sken break; 1350331766Sken } 1351331766Sken case OCS_IO_TYPE_CT_RESP: { 1352331766Sken rc = ocs_hw_srrs_send(&ocs->hw, io->hio_type, io->hio, 1353331766Sken &io->els_rsp, io->wire_len, 1354331766Sken NULL, &io->node->rnode, &io->iparam, 1355331766Sken io->hw_cb, io); 1356331766Sken break; 1357331766Sken } 1358331766Sken case OCS_IO_TYPE_BLS_RESP: { 1359331766Sken /* no need to update tgt_task_tag for BLS response since the RX_ID 1360331766Sken * will be specified by the payload, not the XRI */ 1361331766Sken rc = ocs_hw_srrs_send(&ocs->hw, io->hio_type, io->hio, 1362331766Sken NULL, 0, NULL, &io->node->rnode, &io->iparam, io->hw_cb, io); 1363331766Sken break; 1364331766Sken } 1365331766Sken default: 1366331766Sken scsi_io_printf(io, "Unknown IO type=%d\n", io->io_type); 1367331766Sken rc = -1; 1368331766Sken break; 1369331766Sken } 1370331766Sken return rc; 1371331766Sken} 1372331766Sken 1373331766Sken/** 1374331766Sken * @brief Dispatch IO 1375331766Sken * 1376331766Sken * @par Description 1377331766Sken * An IO that does require a HW IO is dispatched to the HW. 1378331766Sken * 1379331766Sken * @param io Pointer to IO structure. 1380331766Sken * 1381331766Sken * @return Returns 0 on success, or a negative error code value on failure. 1382331766Sken */ 1383331766Sken 1384331766Skenstatic int32_t 1385331766Skenocs_scsi_io_dispatch_no_hw_io(ocs_io_t *io) 1386331766Sken{ 1387331766Sken int32_t rc; 1388331766Sken 1389331766Sken switch (io->io_type) { 1390331766Sken case OCS_IO_TYPE_ABORT: { 1391331766Sken ocs_hw_io_t *hio_to_abort = NULL; 1392331766Sken ocs_assert(io->io_to_abort, -1); 1393331766Sken hio_to_abort = io->io_to_abort->hio; 1394331766Sken 1395331766Sken if (hio_to_abort == NULL) { 1396331766Sken /* 1397331766Sken * If "IO to abort" does not have an associated HW IO, immediately 1398331766Sken * make callback with success. The command must have been sent to 1399331766Sken * the backend, but the data phase has not yet started, so we don't 1400331766Sken * have a HW IO. 1401331766Sken * 1402331766Sken * Note: since the backend shims should be taking a reference 1403331766Sken * on io_to_abort, it should not be possible to have been completed 1404331766Sken * and freed by the backend before the abort got here. 1405331766Sken */ 1406331766Sken scsi_io_printf(io, "IO: " SCSI_IOFMT " not active\n", 1407331766Sken SCSI_IOFMT_ARGS(io->io_to_abort)); 1408331766Sken ((ocs_hw_done_t)io->hw_cb)(io->hio, NULL, 0, SLI4_FC_WCQE_STATUS_SUCCESS, 0, io); 1409331766Sken rc = 0; 1410331766Sken } else { 1411331766Sken /* HW IO is valid, abort it */ 1412331766Sken scsi_io_printf(io, "aborting " SCSI_IOFMT "\n", SCSI_IOFMT_ARGS(io->io_to_abort)); 1413331766Sken rc = ocs_hw_io_abort(&io->ocs->hw, hio_to_abort, io->send_abts, 1414331766Sken io->hw_cb, io); 1415331766Sken if (rc) { 1416331766Sken int status = SLI4_FC_WCQE_STATUS_SUCCESS; 1417331766Sken if ((rc != OCS_HW_RTN_IO_NOT_ACTIVE) && 1418331766Sken (rc != OCS_HW_RTN_IO_ABORT_IN_PROGRESS)) { 1419331766Sken status = -1; 1420331766Sken scsi_io_printf(io, "Failed to abort IO: " SCSI_IOFMT " status=%d\n", 1421331766Sken SCSI_IOFMT_ARGS(io->io_to_abort), rc); 1422331766Sken } 1423331766Sken ((ocs_hw_done_t)io->hw_cb)(io->hio, NULL, 0, status, 0, io); 1424331766Sken rc = 0; 1425331766Sken } 1426331766Sken } 1427331766Sken 1428331766Sken break; 1429331766Sken } 1430331766Sken default: 1431331766Sken scsi_io_printf(io, "Unknown IO type=%d\n", io->io_type); 1432331766Sken rc = -1; 1433331766Sken break; 1434331766Sken } 1435331766Sken return rc; 1436331766Sken} 1437331766Sken 1438331766Sken/** 1439331766Sken * @ingroup scsi_api_base 1440331766Sken * @brief Send read/write data. 1441331766Sken * 1442331766Sken * @par Description 1443331766Sken * This call is made by a target-server to initiate a SCSI read or write data phase, transferring 1444331766Sken * data between the target to the remote initiator. The payload is specified by the 1445331766Sken * scatter-gather list @c sgl of length @c sgl_count. The @c wire_len argument 1446331766Sken * specifies the payload length (independent of the scatter-gather list cumulative length). 1447331766Sken * @n @n 1448331766Sken * The @c flags argument has one bit, OCS_SCSI_LAST_DATAPHASE, which is a hint to the base 1449331766Sken * driver that it may use auto SCSI response features if the hardware supports it. 1450331766Sken * @n @n 1451331766Sken * Upon completion, the callback function @b cb is called with flags indicating that the 1452331766Sken * IO has completed (OCS_SCSI_IO_COMPL) and another data phase or response may be sent; 1453331766Sken * that the IO has completed and no response needs to be sent (OCS_SCSI_IO_COMPL_NO_RSP); 1454331766Sken * or that the IO was aborted (OCS_SCSI_IO_ABORTED). 1455331766Sken * 1456331766Sken * @param io Pointer to the IO context. 1457331766Sken * @param flags Flags controlling the sending of data. 1458331766Sken * @param dif_info Pointer to T10 DIF fields, or NULL if no DIF. 1459331766Sken * @param sgl Pointer to the payload scatter-gather list. 1460331766Sken * @param sgl_count Count of the scatter-gather list elements. 1461331766Sken * @param xwire_len Length of the payload on wire, in bytes. 1462331766Sken * @param type HW IO type. 1463331766Sken * @param enable_ar Enable auto-response if true. 1464331766Sken * @param cb Completion callback. 1465331766Sken * @param arg Application-supplied callback data. 1466331766Sken * 1467331766Sken * @return Returns 0 on success, or a negative error code value on failure. 1468331766Sken */ 1469331766Sken 1470331766Skenstatic inline int32_t 1471331766Skenocs_scsi_xfer_data(ocs_io_t *io, uint32_t flags, 1472331766Sken ocs_scsi_dif_info_t *dif_info, 1473331766Sken ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t xwire_len, 1474331766Sken ocs_hw_io_type_e type, int enable_ar, 1475331766Sken ocs_scsi_io_cb_t cb, void *arg) 1476331766Sken{ 1477331766Sken int32_t rc; 1478331766Sken ocs_t *ocs; 1479331766Sken uint32_t disable_ar_tgt_dif = FALSE; 1480331766Sken size_t residual = 0; 1481331766Sken 1482331766Sken if ((dif_info != NULL) && (dif_info->dif_oper == OCS_SCSI_DIF_OPER_DISABLED)) { 1483331766Sken dif_info = NULL; 1484331766Sken } 1485331766Sken 1486331766Sken ocs_assert(io, -1); 1487331766Sken 1488331766Sken if (dif_info != NULL) { 1489331766Sken ocs_hw_get(&io->ocs->hw, OCS_HW_DISABLE_AR_TGT_DIF, &disable_ar_tgt_dif); 1490331766Sken if (disable_ar_tgt_dif) { 1491331766Sken enable_ar = FALSE; 1492331766Sken } 1493331766Sken } 1494331766Sken 1495331766Sken io->sgl_count = sgl_count; 1496331766Sken 1497331766Sken /* If needed, copy SGL */ 1498331766Sken if (sgl && (sgl != io->sgl)) { 1499331766Sken ocs_assert(sgl_count <= io->sgl_allocated, -1); 1500331766Sken ocs_memcpy(io->sgl, sgl, sgl_count*sizeof(*io->sgl)); 1501331766Sken } 1502331766Sken 1503331766Sken ocs = io->ocs; 1504331766Sken ocs_assert(ocs, -1); 1505331766Sken ocs_assert(io->node, -1); 1506331766Sken 1507331766Sken scsi_io_trace(io, "%s wire_len %d\n", (type == OCS_HW_IO_TARGET_READ) ? "send" : "recv", xwire_len); 1508331766Sken 1509331766Sken ocs_assert(sgl, -1); 1510331766Sken ocs_assert(sgl_count > 0, -1); 1511331766Sken ocs_assert(io->exp_xfer_len > io->transferred, -1); 1512331766Sken 1513331766Sken io->hio_type = type; 1514331766Sken 1515331766Sken io->scsi_tgt_cb = cb; 1516331766Sken io->scsi_tgt_cb_arg = arg; 1517331766Sken 1518331766Sken rc = ocs_scsi_convert_dif_info(ocs, dif_info, &io->hw_dif); 1519331766Sken if (rc) { 1520331766Sken return rc; 1521331766Sken } 1522331766Sken 1523331766Sken /* If DIF is used, then save lba for error recovery */ 1524331766Sken if (dif_info) { 1525331766Sken io->scsi_dif_info = *dif_info; 1526331766Sken } 1527331766Sken 1528331766Sken io->wire_len = MIN(xwire_len, io->exp_xfer_len - io->transferred); 1529331766Sken residual = (xwire_len - io->wire_len); 1530331766Sken 1531331766Sken ocs_memset(&io->iparam, 0, sizeof(io->iparam)); 1532331766Sken io->iparam.fcp_tgt.ox_id = io->init_task_tag; 1533331766Sken io->iparam.fcp_tgt.offset = io->transferred; 1534331766Sken io->iparam.fcp_tgt.dif_oper = io->hw_dif.dif; 1535331766Sken io->iparam.fcp_tgt.blk_size = io->hw_dif.blk_size; 1536331766Sken io->iparam.fcp_tgt.cs_ctl = io->cs_ctl; 1537331766Sken io->iparam.fcp_tgt.timeout = io->timeout; 1538331766Sken 1539331766Sken /* if this is the last data phase and there is no residual, enable 1540331766Sken * auto-good-response 1541331766Sken */ 1542331766Sken if (enable_ar && (flags & OCS_SCSI_LAST_DATAPHASE) && 1543331766Sken (residual == 0) && ((io->transferred + io->wire_len) == io->exp_xfer_len) && (!(flags & OCS_SCSI_NO_AUTO_RESPONSE))) { 1544331766Sken io->iparam.fcp_tgt.flags |= SLI4_IO_AUTO_GOOD_RESPONSE; 1545331766Sken io->auto_resp = TRUE; 1546331766Sken } else { 1547331766Sken io->auto_resp = FALSE; 1548331766Sken } 1549331766Sken 1550331766Sken /* save this transfer length */ 1551331766Sken io->xfer_req = io->wire_len; 1552331766Sken 1553331766Sken /* Adjust the transferred count to account for overrun 1554331766Sken * when the residual is calculated in ocs_scsi_send_resp 1555331766Sken */ 1556331766Sken io->transferred += residual; 1557331766Sken 1558331766Sken /* Adjust the SGL size if there is overrun */ 1559331766Sken 1560331766Sken if (residual) { 1561331766Sken ocs_scsi_sgl_t *sgl_ptr = &io->sgl[sgl_count-1]; 1562331766Sken 1563331766Sken while (residual) { 1564331766Sken size_t len = sgl_ptr->len; 1565331766Sken if ( len > residual) { 1566331766Sken sgl_ptr->len = len - residual; 1567331766Sken residual = 0; 1568331766Sken } else { 1569331766Sken sgl_ptr->len = 0; 1570331766Sken residual -= len; 1571331766Sken io->sgl_count--; 1572331766Sken } 1573331766Sken sgl_ptr--; 1574331766Sken } 1575331766Sken } 1576331766Sken 1577331766Sken /* Set latency and WQ steering */ 1578331766Sken io->low_latency = (flags & OCS_SCSI_LOW_LATENCY) != 0; 1579331766Sken io->wq_steering = (flags & OCS_SCSI_WQ_STEERING_MASK) >> OCS_SCSI_WQ_STEERING_SHIFT; 1580331766Sken io->wq_class = (flags & OCS_SCSI_WQ_CLASS_MASK) >> OCS_SCSI_WQ_CLASS_SHIFT; 1581331766Sken 1582331766Sken return ocs_scsi_io_dispatch(io, ocs_target_io_cb); 1583331766Sken} 1584331766Sken 1585331766Sken 1586331766Skenint32_t 1587331766Skenocs_scsi_send_rd_data(ocs_io_t *io, uint32_t flags, 1588331766Sken ocs_scsi_dif_info_t *dif_info, 1589331766Sken ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t len, 1590331766Sken ocs_scsi_io_cb_t cb, void *arg) 1591331766Sken{ 1592331766Sken return ocs_scsi_xfer_data(io, flags, dif_info, sgl, sgl_count, len, OCS_HW_IO_TARGET_READ, 1593331766Sken enable_tsend_auto_resp(io->ocs), cb, arg); 1594331766Sken} 1595331766Sken 1596331766Skenint32_t 1597331766Skenocs_scsi_recv_wr_data(ocs_io_t *io, uint32_t flags, 1598331766Sken ocs_scsi_dif_info_t *dif_info, 1599331766Sken ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t len, 1600331766Sken ocs_scsi_io_cb_t cb, void *arg) 1601331766Sken{ 1602331766Sken return ocs_scsi_xfer_data(io, flags, dif_info, sgl, sgl_count, len, OCS_HW_IO_TARGET_WRITE, 1603331766Sken enable_treceive_auto_resp(io->ocs), cb, arg); 1604331766Sken} 1605331766Sken 1606331766Sken/** 1607331766Sken * @ingroup scsi_api_base 1608331766Sken * @brief Free overflow SGL. 1609331766Sken * 1610331766Sken * @par Description 1611331766Sken * Free the overflow SGL if it is present. 1612331766Sken * 1613331766Sken * @param io Pointer to IO object. 1614331766Sken * 1615331766Sken * @return None. 1616331766Sken */ 1617331766Skenstatic void 1618331766Skenocs_scsi_io_free_ovfl(ocs_io_t *io) { 1619331766Sken if (io->ovfl_sgl.size) { 1620331766Sken ocs_dma_free(io->ocs, &io->ovfl_sgl); 1621331766Sken } 1622331766Sken} 1623331766Sken 1624331766Sken/** 1625331766Sken * @ingroup scsi_api_base 1626331766Sken * @brief Send response data. 1627331766Sken * 1628331766Sken * @par Description 1629331766Sken * This function is used by a target-server to send the SCSI response data to a remote 1630331766Sken * initiator node. The target-server populates the @c ocs_scsi_cmd_resp_t 1631331766Sken * argument with scsi status, status qualifier, sense data, and response data, as 1632331766Sken * needed. 1633331766Sken * @n @n 1634331766Sken * Upon completion, the callback function @c cb is invoked. The target-server will generally 1635331766Sken * clean up its IO context resources and call ocs_scsi_io_complete(). 1636331766Sken * 1637331766Sken * @param io Pointer to the IO context. 1638331766Sken * @param flags Flags to control sending of the SCSI response. 1639331766Sken * @param rsp Pointer to the response data populated by the caller. 1640331766Sken * @param cb Completion callback. 1641331766Sken * @param arg Application-specified completion callback argument. 1642331766Sken 1643331766Sken * @return Returns 0 on success, or a negative error code value on failure. 1644331766Sken */ 1645331766Skenint32_t 1646331766Skenocs_scsi_send_resp(ocs_io_t *io, uint32_t flags, ocs_scsi_cmd_resp_t *rsp, ocs_scsi_io_cb_t cb, void *arg) 1647331766Sken{ 1648331766Sken ocs_t *ocs; 1649331766Sken int32_t residual; 1650331766Sken int auto_resp = TRUE; /* Always try auto resp */ 1651331766Sken uint8_t scsi_status = 0; 1652331766Sken uint16_t scsi_status_qualifier = 0; 1653331766Sken uint8_t *sense_data = NULL; 1654331766Sken uint32_t sense_data_length = 0; 1655331766Sken 1656331766Sken ocs_assert(io, -1); 1657331766Sken 1658331766Sken ocs = io->ocs; 1659331766Sken ocs_assert(ocs, -1); 1660331766Sken 1661331766Sken ocs_assert(io->node, -1); 1662331766Sken 1663331766Sken ocs_scsi_convert_dif_info(ocs, NULL, &io->hw_dif); 1664331766Sken 1665331766Sken if (rsp) { 1666331766Sken scsi_status = rsp->scsi_status; 1667331766Sken scsi_status_qualifier = rsp->scsi_status_qualifier; 1668331766Sken sense_data = rsp->sense_data; 1669331766Sken sense_data_length = rsp->sense_data_length; 1670331766Sken residual = rsp->residual; 1671331766Sken } else { 1672331766Sken residual = io->exp_xfer_len - io->transferred; 1673331766Sken } 1674331766Sken 1675331766Sken io->wire_len = 0; 1676331766Sken io->hio_type = OCS_HW_IO_TARGET_RSP; 1677331766Sken 1678331766Sken io->scsi_tgt_cb = cb; 1679331766Sken io->scsi_tgt_cb_arg = arg; 1680331766Sken 1681331766Sken ocs_memset(&io->iparam, 0, sizeof(io->iparam)); 1682331766Sken io->iparam.fcp_tgt.ox_id = io->init_task_tag; 1683331766Sken io->iparam.fcp_tgt.offset = 0; 1684331766Sken io->iparam.fcp_tgt.cs_ctl = io->cs_ctl; 1685331766Sken io->iparam.fcp_tgt.timeout = io->timeout; 1686331766Sken 1687331766Sken /* Set low latency queueing request */ 1688331766Sken io->low_latency = (flags & OCS_SCSI_LOW_LATENCY) != 0; 1689331766Sken io->wq_steering = (flags & OCS_SCSI_WQ_STEERING_MASK) >> OCS_SCSI_WQ_STEERING_SHIFT; 1690331766Sken io->wq_class = (flags & OCS_SCSI_WQ_CLASS_MASK) >> OCS_SCSI_WQ_CLASS_SHIFT; 1691331766Sken 1692331766Sken if ((scsi_status != 0) || residual || sense_data_length) { 1693331766Sken fcp_rsp_iu_t *fcprsp = io->rspbuf.virt; 1694331766Sken 1695331766Sken if (!fcprsp) { 1696331766Sken ocs_log_err(ocs, "NULL response buffer\n"); 1697331766Sken return -1; 1698331766Sken } 1699331766Sken 1700331766Sken auto_resp = FALSE; 1701331766Sken 1702331766Sken ocs_memset(fcprsp, 0, sizeof(*fcprsp)); 1703331766Sken 1704331766Sken io->wire_len += (sizeof(*fcprsp) - sizeof(fcprsp->data)); 1705331766Sken 1706331766Sken fcprsp->scsi_status = scsi_status; 1707331766Sken *((uint16_t*)fcprsp->status_qualifier) = ocs_htobe16(scsi_status_qualifier); 1708331766Sken 1709331766Sken /* set residual status if necessary */ 1710331766Sken if (residual != 0) { 1711331766Sken /* FCP: if data transferred is less than the amount expected, then this is an 1712331766Sken * underflow. If data transferred would have been greater than the amount expected 1713331766Sken * then this is an overflow 1714331766Sken */ 1715331766Sken if (residual > 0) { 1716331766Sken fcprsp->flags |= FCP_RESID_UNDER; 1717331766Sken *((uint32_t *)fcprsp->fcp_resid) = ocs_htobe32(residual); 1718331766Sken } else { 1719331766Sken fcprsp->flags |= FCP_RESID_OVER; 1720331766Sken *((uint32_t *)fcprsp->fcp_resid) = ocs_htobe32(-residual); 1721331766Sken } 1722331766Sken } 1723331766Sken 1724331766Sken if (sense_data && sense_data_length) { 1725331766Sken ocs_assert(sense_data_length <= sizeof(fcprsp->data), -1); 1726331766Sken fcprsp->flags |= FCP_SNS_LEN_VALID; 1727331766Sken ocs_memcpy(fcprsp->data, sense_data, sense_data_length); 1728331766Sken *((uint32_t*)fcprsp->fcp_sns_len) = ocs_htobe32(sense_data_length); 1729331766Sken io->wire_len += sense_data_length; 1730331766Sken } 1731331766Sken 1732331766Sken io->sgl[0].addr = io->rspbuf.phys; 1733331766Sken io->sgl[0].dif_addr = 0; 1734331766Sken io->sgl[0].len = io->wire_len; 1735331766Sken io->sgl_count = 1; 1736331766Sken } 1737331766Sken 1738331766Sken if (auto_resp) { 1739331766Sken io->iparam.fcp_tgt.flags |= SLI4_IO_AUTO_GOOD_RESPONSE; 1740331766Sken } 1741331766Sken 1742331766Sken return ocs_scsi_io_dispatch(io, ocs_target_io_cb); 1743331766Sken} 1744331766Sken 1745331766Sken/** 1746331766Sken * @ingroup scsi_api_base 1747331766Sken * @brief Send TMF response data. 1748331766Sken * 1749331766Sken * @par Description 1750331766Sken * This function is used by a target-server to send SCSI TMF response data to a remote 1751331766Sken * initiator node. 1752331766Sken * Upon completion, the callback function @c cb is invoked. The target-server will generally 1753331766Sken * clean up its IO context resources and call ocs_scsi_io_complete(). 1754331766Sken * 1755331766Sken * @param io Pointer to the IO context. 1756331766Sken * @param rspcode TMF response code. 1757331766Sken * @param addl_rsp_info Additional TMF response information (may be NULL for zero data). 1758331766Sken * @param cb Completion callback. 1759331766Sken * @param arg Application-specified completion callback argument. 1760331766Sken * 1761331766Sken * @return Returns 0 on success, or a negative error code value on failure. 1762331766Sken */ 1763331766Skenint32_t 1764331766Skenocs_scsi_send_tmf_resp(ocs_io_t *io, ocs_scsi_tmf_resp_e rspcode, uint8_t addl_rsp_info[3], 1765331766Sken ocs_scsi_io_cb_t cb, void *arg) 1766331766Sken{ 1767331766Sken int32_t rc = -1; 1768331766Sken ocs_t *ocs = NULL; 1769331766Sken fcp_rsp_iu_t *fcprsp = NULL; 1770331766Sken fcp_rsp_info_t *rspinfo = NULL; 1771331766Sken uint8_t fcp_rspcode; 1772331766Sken 1773331766Sken ocs_assert(io, -1); 1774331766Sken ocs_assert(io->ocs, -1); 1775331766Sken ocs_assert(io->node, -1); 1776331766Sken 1777331766Sken ocs = io->ocs; 1778331766Sken 1779331766Sken io->wire_len = 0; 1780331766Sken ocs_scsi_convert_dif_info(ocs, NULL, &io->hw_dif); 1781331766Sken 1782331766Sken switch(rspcode) { 1783331766Sken case OCS_SCSI_TMF_FUNCTION_COMPLETE: 1784331766Sken fcp_rspcode = FCP_TMF_COMPLETE; 1785331766Sken break; 1786331766Sken case OCS_SCSI_TMF_FUNCTION_SUCCEEDED: 1787331766Sken case OCS_SCSI_TMF_FUNCTION_IO_NOT_FOUND: 1788331766Sken fcp_rspcode = FCP_TMF_SUCCEEDED; 1789331766Sken break; 1790331766Sken case OCS_SCSI_TMF_FUNCTION_REJECTED: 1791331766Sken fcp_rspcode = FCP_TMF_REJECTED; 1792331766Sken break; 1793331766Sken case OCS_SCSI_TMF_INCORRECT_LOGICAL_UNIT_NUMBER: 1794331766Sken fcp_rspcode = FCP_TMF_INCORRECT_LUN; 1795331766Sken break; 1796331766Sken case OCS_SCSI_TMF_SERVICE_DELIVERY: 1797331766Sken fcp_rspcode = FCP_TMF_FAILED; 1798331766Sken break; 1799331766Sken default: 1800331766Sken fcp_rspcode = FCP_TMF_REJECTED; 1801331766Sken break; 1802331766Sken } 1803331766Sken 1804331766Sken io->hio_type = OCS_HW_IO_TARGET_RSP; 1805331766Sken 1806331766Sken io->scsi_tgt_cb = cb; 1807331766Sken io->scsi_tgt_cb_arg = arg; 1808331766Sken 1809331766Sken if (io->tmf_cmd == OCS_SCSI_TMF_ABORT_TASK) { 1810331766Sken rc = ocs_target_send_bls_resp(io, cb, arg); 1811331766Sken return rc; 1812331766Sken } 1813331766Sken 1814331766Sken /* populate the FCP TMF response */ 1815331766Sken fcprsp = io->rspbuf.virt; 1816331766Sken ocs_memset(fcprsp, 0, sizeof(*fcprsp)); 1817331766Sken 1818331766Sken fcprsp->flags |= FCP_RSP_LEN_VALID; 1819331766Sken 1820331766Sken rspinfo = (fcp_rsp_info_t*) fcprsp->data; 1821331766Sken if (addl_rsp_info != NULL) { 1822331766Sken ocs_memcpy(rspinfo->addl_rsp_info, addl_rsp_info, sizeof(rspinfo->addl_rsp_info)); 1823331766Sken } 1824331766Sken rspinfo->rsp_code = fcp_rspcode; 1825331766Sken 1826331766Sken io->wire_len = sizeof(*fcprsp) - sizeof(fcprsp->data) + sizeof(*rspinfo); 1827331766Sken 1828331766Sken *((uint32_t*)fcprsp->fcp_rsp_len) = ocs_htobe32(sizeof(*rspinfo)); 1829331766Sken 1830331766Sken io->sgl[0].addr = io->rspbuf.phys; 1831331766Sken io->sgl[0].dif_addr = 0; 1832331766Sken io->sgl[0].len = io->wire_len; 1833331766Sken io->sgl_count = 1; 1834331766Sken 1835331766Sken ocs_memset(&io->iparam, 0, sizeof(io->iparam)); 1836331766Sken io->iparam.fcp_tgt.ox_id = io->init_task_tag; 1837331766Sken io->iparam.fcp_tgt.offset = 0; 1838331766Sken io->iparam.fcp_tgt.cs_ctl = io->cs_ctl; 1839331766Sken io->iparam.fcp_tgt.timeout = io->timeout; 1840331766Sken 1841331766Sken rc = ocs_scsi_io_dispatch(io, ocs_target_io_cb); 1842331766Sken 1843331766Sken return rc; 1844331766Sken} 1845331766Sken 1846331766Sken 1847331766Sken/** 1848331766Sken * @brief Process target abort callback. 1849331766Sken * 1850331766Sken * @par Description 1851331766Sken * Accepts HW abort requests. 1852331766Sken * 1853331766Sken * @param hio HW IO context. 1854331766Sken * @param rnode Remote node. 1855331766Sken * @param length Length of response data. 1856331766Sken * @param status Completion status. 1857331766Sken * @param ext_status Extended completion status. 1858331766Sken * @param app Application-specified callback data. 1859331766Sken * 1860331766Sken * @return Returns 0 on success, or a negative error code value on failure. 1861331766Sken */ 1862331766Sken 1863331766Skenstatic int32_t 1864331766Skenocs_target_abort_cb(ocs_hw_io_t *hio, ocs_remote_node_t *rnode, uint32_t length, int32_t status, uint32_t ext_status, void *app) 1865331766Sken{ 1866331766Sken ocs_io_t *io = app; 1867331766Sken ocs_t *ocs; 1868331766Sken ocs_scsi_io_status_e scsi_status; 1869331766Sken 1870331766Sken ocs_assert(io, -1); 1871331766Sken ocs_assert(io->ocs, -1); 1872331766Sken 1873331766Sken ocs = io->ocs; 1874331766Sken 1875331766Sken if (io->abort_cb) { 1876331766Sken ocs_scsi_io_cb_t abort_cb = io->abort_cb; 1877331766Sken void *abort_cb_arg = io->abort_cb_arg; 1878331766Sken 1879331766Sken io->abort_cb = NULL; 1880331766Sken io->abort_cb_arg = NULL; 1881331766Sken 1882331766Sken switch (status) { 1883331766Sken case SLI4_FC_WCQE_STATUS_SUCCESS: 1884331766Sken scsi_status = OCS_SCSI_STATUS_GOOD; 1885331766Sken break; 1886331766Sken case SLI4_FC_WCQE_STATUS_LOCAL_REJECT: 1887331766Sken switch (ext_status) { 1888331766Sken case SLI4_FC_LOCAL_REJECT_NO_XRI: 1889331766Sken scsi_status = OCS_SCSI_STATUS_NO_IO; 1890331766Sken break; 1891331766Sken case SLI4_FC_LOCAL_REJECT_ABORT_IN_PROGRESS: 1892331766Sken scsi_status = OCS_SCSI_STATUS_ABORT_IN_PROGRESS; 1893331766Sken break; 1894331766Sken default: 1895331766Sken /* TODO: we have seen 0x15 (abort in progress) */ 1896331766Sken scsi_status = OCS_SCSI_STATUS_ERROR; 1897331766Sken break; 1898331766Sken } 1899331766Sken break; 1900331766Sken case SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE: 1901331766Sken scsi_status = OCS_SCSI_STATUS_CHECK_RESPONSE; 1902331766Sken break; 1903331766Sken default: 1904331766Sken scsi_status = OCS_SCSI_STATUS_ERROR; 1905331766Sken break; 1906331766Sken } 1907331766Sken /* invoke callback */ 1908331766Sken abort_cb(io->io_to_abort, scsi_status, 0, abort_cb_arg); 1909331766Sken } 1910331766Sken 1911331766Sken ocs_assert(io != io->io_to_abort, -1); 1912331766Sken 1913331766Sken /* done with IO to abort */ 1914331766Sken ocs_ref_put(&io->io_to_abort->ref); /* ocs_ref_get(): ocs_scsi_tgt_abort_io() */ 1915331766Sken 1916331766Sken ocs_io_free(ocs, io); 1917331766Sken 1918331766Sken ocs_scsi_check_pending(ocs); 1919331766Sken return 0; 1920331766Sken} 1921331766Sken 1922331766Sken/** 1923331766Sken * @ingroup scsi_api_base 1924331766Sken * @brief Abort a target IO. 1925331766Sken * 1926331766Sken * @par Description 1927331766Sken * This routine is called from a SCSI target-server. It initiates an abort of a 1928331766Sken * previously-issued target data phase or response request. 1929331766Sken * 1930331766Sken * @param io IO context. 1931331766Sken * @param cb SCSI target server callback. 1932331766Sken * @param arg SCSI target server supplied callback argument. 1933331766Sken * 1934331766Sken * @return Returns 0 on success, or a non-zero value on failure. 1935331766Sken */ 1936331766Skenint32_t 1937331766Skenocs_scsi_tgt_abort_io(ocs_io_t *io, ocs_scsi_io_cb_t cb, void *arg) 1938331766Sken{ 1939331766Sken ocs_t *ocs; 1940331766Sken ocs_xport_t *xport; 1941331766Sken int32_t rc; 1942331766Sken 1943331766Sken ocs_io_t *abort_io = NULL; 1944331766Sken ocs_assert(io, -1); 1945331766Sken ocs_assert(io->node, -1); 1946331766Sken ocs_assert(io->ocs, -1); 1947331766Sken 1948331766Sken ocs = io->ocs; 1949331766Sken xport = ocs->xport; 1950331766Sken 1951331766Sken /* take a reference on IO being aborted */ 1952331766Sken if ((ocs_ref_get_unless_zero(&io->ref) == 0)) { 1953331766Sken /* command no longer active */ 1954331766Sken scsi_io_printf(io, "command no longer active\n"); 1955331766Sken return -1; 1956331766Sken } 1957331766Sken 1958331766Sken /* 1959331766Sken * allocate a new IO to send the abort request. Use ocs_io_alloc() directly, as 1960331766Sken * we need an IO object that will not fail allocation due to allocations being 1961331766Sken * disabled (in ocs_scsi_io_alloc()) 1962331766Sken */ 1963331766Sken abort_io = ocs_io_alloc(ocs); 1964331766Sken if (abort_io == NULL) { 1965331766Sken ocs_atomic_add_return(&xport->io_alloc_failed_count, 1); 1966331766Sken ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */ 1967331766Sken return -1; 1968331766Sken } 1969331766Sken 1970331766Sken /* Save the target server callback and argument */ 1971331766Sken ocs_assert(abort_io->hio == NULL, -1); 1972331766Sken 1973331766Sken /* set generic fields */ 1974331766Sken abort_io->cmd_tgt = TRUE; 1975331766Sken abort_io->node = io->node; 1976331766Sken 1977331766Sken /* set type and abort-specific fields */ 1978331766Sken abort_io->io_type = OCS_IO_TYPE_ABORT; 1979331766Sken abort_io->display_name = "tgt_abort"; 1980331766Sken abort_io->io_to_abort = io; 1981331766Sken abort_io->send_abts = FALSE; 1982331766Sken abort_io->abort_cb = cb; 1983331766Sken abort_io->abort_cb_arg = arg; 1984331766Sken 1985331766Sken /* now dispatch IO */ 1986331766Sken rc = ocs_scsi_io_dispatch_abort(abort_io, ocs_target_abort_cb); 1987331766Sken if (rc) { 1988331766Sken ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */ 1989331766Sken } 1990331766Sken return rc; 1991331766Sken} 1992331766Sken 1993331766Sken/** 1994331766Sken * @brief Process target BLS response callback. 1995331766Sken * 1996331766Sken * @par Description 1997331766Sken * Accepts HW abort requests. 1998331766Sken * 1999331766Sken * @param hio HW IO context. 2000331766Sken * @param rnode Remote node. 2001331766Sken * @param length Length of response data. 2002331766Sken * @param status Completion status. 2003331766Sken * @param ext_status Extended completion status. 2004331766Sken * @param app Application-specified callback data. 2005331766Sken * 2006331766Sken * @return Returns 0 on success, or a negative error code value on failure. 2007331766Sken */ 2008331766Sken 2009331766Skenstatic int32_t 2010331766Skenocs_target_bls_resp_cb(ocs_hw_io_t *hio, ocs_remote_node_t *rnode, uint32_t length, int32_t status, uint32_t ext_status, void *app) 2011331766Sken{ 2012331766Sken ocs_io_t *io = app; 2013331766Sken ocs_t *ocs; 2014331766Sken ocs_scsi_io_status_e bls_status; 2015331766Sken 2016331766Sken ocs_assert(io, -1); 2017331766Sken ocs_assert(io->ocs, -1); 2018331766Sken 2019331766Sken ocs = io->ocs; 2020331766Sken 2021331766Sken /* BLS isn't really a "SCSI" concept, but use SCSI status */ 2022331766Sken if (status) { 2023331766Sken io_error_log(io, "s=%#x x=%#x\n", status, ext_status); 2024331766Sken bls_status = OCS_SCSI_STATUS_ERROR; 2025331766Sken } else { 2026331766Sken bls_status = OCS_SCSI_STATUS_GOOD; 2027331766Sken } 2028331766Sken 2029331766Sken if (io->bls_cb) { 2030331766Sken ocs_scsi_io_cb_t bls_cb = io->bls_cb; 2031331766Sken void *bls_cb_arg = io->bls_cb_arg; 2032331766Sken 2033331766Sken io->bls_cb = NULL; 2034331766Sken io->bls_cb_arg = NULL; 2035331766Sken 2036331766Sken /* invoke callback */ 2037331766Sken bls_cb(io, bls_status, 0, bls_cb_arg); 2038331766Sken } 2039331766Sken 2040331766Sken ocs_scsi_check_pending(ocs); 2041331766Sken return 0; 2042331766Sken} 2043331766Sken 2044331766Sken/** 2045331766Sken * @brief Complete abort request. 2046331766Sken * 2047331766Sken * @par Description 2048331766Sken * An abort request is completed by posting a BA_ACC for the IO that requested the abort. 2049331766Sken * 2050331766Sken * @param io Pointer to the IO context. 2051331766Sken * @param cb Callback function to invoke upon completion. 2052331766Sken * @param arg Application-specified completion callback argument. 2053331766Sken * 2054331766Sken * @return Returns 0 on success, or a negative error code value on failure. 2055331766Sken */ 2056331766Sken 2057331766Skenstatic int32_t 2058331766Skenocs_target_send_bls_resp(ocs_io_t *io, ocs_scsi_io_cb_t cb, void *arg) 2059331766Sken{ 2060331766Sken int32_t rc; 2061331766Sken fc_ba_acc_payload_t *acc; 2062331766Sken 2063331766Sken ocs_assert(io, -1); 2064331766Sken 2065331766Sken /* fill out IO structure with everything needed to send BA_ACC */ 2066331766Sken ocs_memset(&io->iparam, 0, sizeof(io->iparam)); 2067331766Sken io->iparam.bls.ox_id = io->init_task_tag; 2068331766Sken io->iparam.bls.rx_id = io->abort_rx_id; 2069331766Sken 2070331766Sken acc = (void *)io->iparam.bls.payload; 2071331766Sken 2072331766Sken ocs_memset(io->iparam.bls.payload, 0, sizeof(io->iparam.bls.payload)); 2073331766Sken acc->ox_id = io->iparam.bls.ox_id; 2074331766Sken acc->rx_id = io->iparam.bls.rx_id; 2075331766Sken acc->high_seq_cnt = UINT16_MAX; 2076331766Sken 2077331766Sken /* generic io fields have already been populated */ 2078331766Sken 2079331766Sken /* set type and BLS-specific fields */ 2080331766Sken io->io_type = OCS_IO_TYPE_BLS_RESP; 2081331766Sken io->display_name = "bls_rsp"; 2082331766Sken io->hio_type = OCS_HW_BLS_ACC; 2083331766Sken io->bls_cb = cb; 2084331766Sken io->bls_cb_arg = arg; 2085331766Sken 2086331766Sken /* dispatch IO */ 2087331766Sken rc = ocs_scsi_io_dispatch(io, ocs_target_bls_resp_cb); 2088331766Sken return rc; 2089331766Sken} 2090331766Sken 2091331766Sken/** 2092331766Sken * @ingroup scsi_api_base 2093331766Sken * @brief Notify the base driver that the IO is complete. 2094331766Sken * 2095331766Sken * @par Description 2096331766Sken * This function is called by a target-server to notify the base driver that an IO 2097331766Sken * has completed, allowing for the base driver to free resources. 2098331766Sken * @n 2099331766Sken * @n @b Note: This function is not called by initiator-clients. 2100331766Sken * 2101331766Sken * @param io Pointer to IO context. 2102331766Sken * 2103331766Sken * @return None. 2104331766Sken */ 2105331766Skenvoid 2106331766Skenocs_scsi_io_complete(ocs_io_t *io) 2107331766Sken{ 2108331766Sken ocs_assert(io); 2109331766Sken 2110331766Sken if (!ocs_io_busy(io)) { 2111331766Sken ocs_log_test(io->ocs, "Got completion for non-busy io with tag 0x%x\n", io->tag); 2112331766Sken return; 2113331766Sken } 2114331766Sken 2115331766Sken scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name); 2116331766Sken ocs_assert(ocs_ref_read_count(&io->ref) > 0); 2117331766Sken ocs_ref_put(&io->ref); /* ocs_ref_get(): ocs_scsi_io_alloc() */ 2118331766Sken} 2119331766Sken 2120331766Sken 2121331766Sken/** 2122331766Sken * @brief Handle initiator IO completion. 2123331766Sken * 2124331766Sken * @par Description 2125331766Sken * This callback is made upon completion of an initiator operation (initiator read/write command). 2126331766Sken * 2127331766Sken * @param hio HW IO context. 2128331766Sken * @param rnode Remote node. 2129331766Sken * @param length Length of completion data. 2130331766Sken * @param status Completion status. 2131331766Sken * @param ext_status Extended completion status. 2132331766Sken * @param app Application-specified callback data. 2133331766Sken * 2134331766Sken * @return None. 2135331766Sken */ 2136331766Sken 2137331766Skenstatic void 2138331766Skenocs_initiator_io_cb(ocs_hw_io_t *hio, ocs_remote_node_t *rnode, uint32_t length, 2139331766Sken int32_t status, uint32_t ext_status, void *app) 2140331766Sken{ 2141331766Sken ocs_io_t *io = app; 2142331766Sken ocs_t *ocs; 2143331766Sken ocs_scsi_io_status_e scsi_status; 2144331766Sken 2145331766Sken ocs_assert(io); 2146331766Sken ocs_assert(io->scsi_ini_cb); 2147331766Sken 2148331766Sken scsi_io_trace(io, "status x%x ext_status x%x\n", status, ext_status); 2149331766Sken 2150331766Sken ocs = io->ocs; 2151331766Sken ocs_assert(ocs); 2152331766Sken 2153331766Sken ocs_scsi_io_free_ovfl(io); 2154331766Sken 2155331766Sken /* Call target server completion */ 2156331766Sken if (io->scsi_ini_cb) { 2157331766Sken fcp_rsp_iu_t *fcprsp = io->rspbuf.virt; 2158331766Sken ocs_scsi_cmd_resp_t rsp; 2159331766Sken ocs_scsi_rsp_io_cb_t cb = io->scsi_ini_cb; 2160331766Sken uint32_t flags = 0; 2161331766Sken uint8_t *pd = fcprsp->data; 2162331766Sken 2163331766Sken /* Clear the callback before invoking the callback */ 2164331766Sken io->scsi_ini_cb = NULL; 2165331766Sken 2166331766Sken ocs_memset(&rsp, 0, sizeof(rsp)); 2167331766Sken 2168331766Sken /* Unless status is FCP_RSP_FAILURE, fcprsp is not filled in */ 2169331766Sken switch (status) { 2170331766Sken case SLI4_FC_WCQE_STATUS_SUCCESS: 2171331766Sken scsi_status = OCS_SCSI_STATUS_GOOD; 2172331766Sken break; 2173331766Sken case SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE: 2174331766Sken scsi_status = OCS_SCSI_STATUS_CHECK_RESPONSE; 2175331766Sken rsp.scsi_status = fcprsp->scsi_status; 2176331766Sken rsp.scsi_status_qualifier = ocs_be16toh(*((uint16_t*)fcprsp->status_qualifier)); 2177331766Sken 2178331766Sken if (fcprsp->flags & FCP_RSP_LEN_VALID) { 2179331766Sken rsp.response_data = pd; 2180331766Sken rsp.response_data_length = ocs_fc_getbe32(fcprsp->fcp_rsp_len); 2181331766Sken pd += rsp.response_data_length; 2182331766Sken } 2183331766Sken if (fcprsp->flags & FCP_SNS_LEN_VALID) { 2184331766Sken uint32_t sns_len = ocs_fc_getbe32(fcprsp->fcp_sns_len); 2185331766Sken rsp.sense_data = pd; 2186331766Sken rsp.sense_data_length = sns_len; 2187331766Sken pd += sns_len; 2188331766Sken } 2189331766Sken /* Set residual */ 2190331766Sken if (fcprsp->flags & FCP_RESID_OVER) { 2191331766Sken rsp.residual = -ocs_fc_getbe32(fcprsp->fcp_resid); 2192331766Sken rsp.response_wire_length = length; 2193331766Sken } else if (fcprsp->flags & FCP_RESID_UNDER) { 2194331766Sken rsp.residual = ocs_fc_getbe32(fcprsp->fcp_resid); 2195331766Sken rsp.response_wire_length = length; 2196331766Sken } 2197331766Sken 2198331766Sken /* 2199331766Sken * Note: The FCP_RSP_FAILURE can be returned for initiator IOs when the total data 2200331766Sken * placed does not match the requested length even if the status is good. If 2201331766Sken * the status is all zeroes, then we have to assume that a frame(s) were 2202331766Sken * dropped and change the status to LOCAL_REJECT/OUT_OF_ORDER_DATA 2203331766Sken */ 2204331766Sken if (length != io->wire_len) { 2205331766Sken uint32_t rsp_len = ext_status; 2206331766Sken uint8_t *rsp_bytes = io->rspbuf.virt; 2207331766Sken uint32_t i; 2208331766Sken uint8_t all_zeroes = (rsp_len > 0); 2209331766Sken /* Check if the rsp is zero */ 2210331766Sken for (i = 0; i < rsp_len; i++) { 2211331766Sken if (rsp_bytes[i] != 0) { 2212331766Sken all_zeroes = FALSE; 2213331766Sken break; 2214331766Sken } 2215331766Sken } 2216331766Sken if (all_zeroes) { 2217331766Sken scsi_status = OCS_SCSI_STATUS_ERROR; 2218331766Sken ocs_log_test(io->ocs, "[%s]" SCSI_IOFMT "local reject=0x%02x\n", 2219331766Sken io->node->display_name, SCSI_IOFMT_ARGS(io), 2220331766Sken SLI4_FC_LOCAL_REJECT_OUT_OF_ORDER_DATA); 2221331766Sken } 2222331766Sken } 2223331766Sken break; 2224331766Sken case SLI4_FC_WCQE_STATUS_LOCAL_REJECT: 2225331766Sken if (ext_status == SLI4_FC_LOCAL_REJECT_SEQUENCE_TIMEOUT) { 2226331766Sken scsi_status = OCS_SCSI_STATUS_COMMAND_TIMEOUT; 2227331766Sken } else { 2228331766Sken scsi_status = OCS_SCSI_STATUS_ERROR; 2229331766Sken } 2230331766Sken break; 2231331766Sken case SLI4_FC_WCQE_STATUS_DI_ERROR: 2232331766Sken if (ext_status & 0x01) { 2233331766Sken scsi_status = OCS_SCSI_STATUS_DIF_GUARD_ERROR; 2234331766Sken } else if (ext_status & 0x02) { 2235331766Sken scsi_status = OCS_SCSI_STATUS_DIF_APP_TAG_ERROR; 2236331766Sken } else if (ext_status & 0x04) { 2237331766Sken scsi_status = OCS_SCSI_STATUS_DIF_REF_TAG_ERROR; 2238331766Sken } else { 2239331766Sken scsi_status = OCS_SCSI_STATUS_DIF_UNKNOWN_ERROR; 2240331766Sken } 2241331766Sken break; 2242331766Sken default: 2243331766Sken scsi_status = OCS_SCSI_STATUS_ERROR; 2244331766Sken break; 2245331766Sken } 2246331766Sken 2247331766Sken cb(io, scsi_status, &rsp, flags, io->scsi_ini_cb_arg); 2248331766Sken 2249331766Sken } 2250331766Sken ocs_scsi_check_pending(ocs); 2251331766Sken} 2252331766Sken 2253331766Sken/** 2254331766Sken * @ingroup scsi_api_base 2255331766Sken * @brief Initiate initiator read IO. 2256331766Sken * 2257331766Sken * @par Description 2258331766Sken * This call is made by an initiator-client to send a SCSI read command. The payload 2259331766Sken * for the command is given by a scatter-gather list @c sgl for @c sgl_count 2260331766Sken * entries. 2261331766Sken * @n @n 2262331766Sken * Upon completion, the callback @b cb is invoked and passed request status. 2263331766Sken * If the command completed successfully, the callback is given SCSI response data. 2264331766Sken * 2265331766Sken * @param node Pointer to the node. 2266331766Sken * @param io Pointer to the IO context. 2267331766Sken * @param lun LUN value. 2268331766Sken * @param cdb Pointer to the CDB. 2269331766Sken * @param cdb_len Length of the CDB. 2270331766Sken * @param dif_info Pointer to the T10 DIF fields, or NULL if no DIF. 2271331766Sken * @param sgl Pointer to the scatter-gather list. 2272331766Sken * @param sgl_count Count of the scatter-gather list elements. 2273331766Sken * @param wire_len Length of the payload. 2274331766Sken * @param cb Completion callback. 2275331766Sken * @param arg Application-specified completion callback argument. 2276331766Sken * 2277331766Sken * @return Returns 0 on success, or a negative error code value on failure. 2278331766Sken */ 2279331766Skenint32_t 2280331766Skenocs_scsi_send_rd_io(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len, 2281331766Sken ocs_scsi_dif_info_t *dif_info, 2282331766Sken ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, 2283331766Sken ocs_scsi_rsp_io_cb_t cb, void *arg) 2284331766Sken{ 2285331766Sken int32_t rc; 2286331766Sken 2287331766Sken rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_READ, node, io, lun, 0, cdb, cdb_len, dif_info, sgl, sgl_count, 2288331766Sken wire_len, 0, cb, arg); 2289331766Sken 2290331766Sken return rc; 2291331766Sken} 2292331766Sken 2293331766Sken/** 2294331766Sken * @ingroup scsi_api_base 2295331766Sken * @brief Initiate initiator write IO. 2296331766Sken * 2297331766Sken * @par Description 2298331766Sken * This call is made by an initiator-client to send a SCSI write command. The payload 2299331766Sken * for the command is given by a scatter-gather list @c sgl for @c sgl_count 2300331766Sken * entries. 2301331766Sken * @n @n 2302331766Sken * Upon completion, the callback @c cb is invoked and passed request status. If the command 2303331766Sken * completed successfully, the callback is given SCSI response data. 2304331766Sken * 2305331766Sken * @param node Pointer to the node. 2306331766Sken * @param io Pointer to IO context. 2307331766Sken * @param lun LUN value. 2308331766Sken * @param cdb Pointer to the CDB. 2309331766Sken * @param cdb_len Length of the CDB. 2310331766Sken * @param dif_info Pointer to the T10 DIF fields, or NULL if no DIF. 2311331766Sken * @param sgl Pointer to the scatter-gather list. 2312331766Sken * @param sgl_count Count of the scatter-gather list elements. 2313331766Sken * @param wire_len Length of the payload. 2314331766Sken * @param cb Completion callback. 2315331766Sken * @param arg Application-specified completion callback argument. 2316331766Sken * 2317331766Sken * @return Returns 0 on success, or a negative error code value on failure. 2318331766Sken */ 2319331766Skenint32_t ocs_scsi_send_wr_io(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len, 2320331766Sken ocs_scsi_dif_info_t *dif_info, 2321331766Sken ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, 2322331766Sken ocs_scsi_rsp_io_cb_t cb, void *arg) 2323331766Sken{ 2324331766Sken int32_t rc; 2325331766Sken 2326331766Sken rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_WRITE, node, io, lun, 0, cdb, cdb_len, dif_info, sgl, sgl_count, 2327331766Sken wire_len, 0, cb, arg); 2328331766Sken 2329331766Sken return rc; 2330331766Sken} 2331331766Sken 2332331766Sken/** 2333331766Sken * @ingroup scsi_api_base 2334331766Sken * @brief Initiate initiator write IO. 2335331766Sken * 2336331766Sken * @par Description 2337331766Sken * This call is made by an initiator-client to send a SCSI write command. The payload 2338331766Sken * for the command is given by a scatter-gather list @c sgl for @c sgl_count 2339331766Sken * entries. 2340331766Sken * @n @n 2341331766Sken * Upon completion, the callback @c cb is invoked and passed request status. If the command 2342331766Sken * completed successfully, the callback is given SCSI response data. 2343331766Sken * 2344331766Sken * @param node Pointer to the node. 2345331766Sken * @param io Pointer to IO context. 2346331766Sken * @param lun LUN value. 2347331766Sken * @param cdb Pointer to the CDB. 2348331766Sken * @param cdb_len Length of the CDB. 2349331766Sken * @param dif_info Pointer to the T10 DIF fields, or NULL if no DIF. 2350331766Sken * @param sgl Pointer to the scatter-gather list. 2351331766Sken * @param sgl_count Count of the scatter-gather list elements. 2352331766Sken * @param wire_len Length of the payload. 2353331766Sken * @param first_burst Number of first burst bytes to send. 2354331766Sken * @param cb Completion callback. 2355331766Sken * @param arg Application-specified completion callback argument. 2356331766Sken * 2357331766Sken * @return Returns 0 on success, or a negative error code value on failure. 2358331766Sken */ 2359331766Skenint32_t 2360331766Skenocs_scsi_send_wr_io_first_burst(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len, 2361331766Sken ocs_scsi_dif_info_t *dif_info, 2362331766Sken ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, uint32_t first_burst, 2363331766Sken ocs_scsi_rsp_io_cb_t cb, void *arg) 2364331766Sken{ 2365331766Sken int32_t rc; 2366331766Sken 2367331766Sken rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_WRITE, node, io, lun, 0, cdb, cdb_len, dif_info, sgl, sgl_count, 2368331766Sken wire_len, 0, cb, arg); 2369331766Sken 2370331766Sken return rc; 2371331766Sken} 2372331766Sken 2373331766Sken/** 2374331766Sken * @ingroup scsi_api_base 2375331766Sken * @brief Initiate initiator SCSI command with no data. 2376331766Sken * 2377331766Sken * @par Description 2378331766Sken * This call is made by an initiator-client to send a SCSI command with no data. 2379331766Sken * @n @n 2380331766Sken * Upon completion, the callback @c cb is invoked and passed request status. If the command 2381331766Sken * completed successfully, the callback is given SCSI response data. 2382331766Sken * 2383331766Sken * @param node Pointer to the node. 2384331766Sken * @param io Pointer to the IO context. 2385331766Sken * @param lun LUN value. 2386331766Sken * @param cdb Pointer to the CDB. 2387331766Sken * @param cdb_len Length of the CDB. 2388331766Sken * @param cb Completion callback. 2389331766Sken * @param arg Application-specified completion callback argument. 2390331766Sken * 2391331766Sken * @return Returns 0 on success, or a negative error code value on failure. 2392331766Sken */ 2393331766Skenint32_t ocs_scsi_send_nodata_io(ocs_node_t *node, ocs_io_t *io, uint64_t lun, void *cdb, uint32_t cdb_len, 2394331766Sken ocs_scsi_rsp_io_cb_t cb, void *arg) 2395331766Sken{ 2396331766Sken int32_t rc; 2397331766Sken 2398331766Sken rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_NODATA, node, io, lun, 0, cdb, cdb_len, NULL, NULL, 0, 0, 0, cb, arg); 2399331766Sken 2400331766Sken return rc; 2401331766Sken} 2402331766Sken/** 2403331766Sken * @ingroup scsi_api_base 2404331766Sken * @brief Initiate initiator task management operation. 2405331766Sken * 2406331766Sken * @par Description 2407331766Sken * This command is used to send a SCSI task management function command. If the command 2408331766Sken * requires it (QUERY_TASK_SET for example), a payload may be associated with the command. 2409331766Sken * If no payload is required, then @c sgl_count may be zero and @c sgl is ignored. 2410331766Sken * @n @n 2411331766Sken * Upon completion @c cb is invoked with status and SCSI response data. 2412331766Sken * 2413331766Sken * @param node Pointer to the node. 2414331766Sken * @param io Pointer to the IO context. 2415331766Sken * @param io_to_abort Pointer to the IO context to abort in the 2416331766Sken * case of OCS_SCSI_TMF_ABORT_TASK. Note: this can point to the 2417331766Sken * same the same ocs_io_t as @c io, provided that @c io does not 2418331766Sken * have any outstanding work requests. 2419331766Sken * @param lun LUN value. 2420331766Sken * @param tmf Task management command. 2421331766Sken * @param sgl Pointer to the scatter-gather list. 2422331766Sken * @param sgl_count Count of the scatter-gather list elements. 2423331766Sken * @param len Length of the payload. 2424331766Sken * @param cb Completion callback. 2425331766Sken * @param arg Application-specified completion callback argument. 2426331766Sken * 2427331766Sken * @return Returns 0 on success, or a negative error code value on failure. 2428331766Sken */ 2429331766Skenint32_t 2430331766Skenocs_scsi_send_tmf(ocs_node_t *node, ocs_io_t *io, ocs_io_t *io_to_abort, uint64_t lun, ocs_scsi_tmf_cmd_e tmf, 2431331766Sken ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t len, ocs_scsi_rsp_io_cb_t cb, void *arg) 2432331766Sken{ 2433331766Sken int32_t rc; 2434331766Sken ocs_assert(io, -1); 2435331766Sken 2436331766Sken if (tmf == OCS_SCSI_TMF_ABORT_TASK) { 2437331766Sken ocs_assert(io_to_abort, -1); 2438331766Sken 2439331766Sken /* take a reference on IO being aborted */ 2440331766Sken if ((ocs_ref_get_unless_zero(&io_to_abort->ref) == 0)) { 2441331766Sken /* command no longer active */ 2442331766Sken scsi_io_printf(io, "command no longer active\n"); 2443331766Sken return -1; 2444331766Sken } 2445331766Sken /* generic io fields have already been populated */ 2446331766Sken 2447331766Sken /* abort-specific fields */ 2448331766Sken io->io_type = OCS_IO_TYPE_ABORT; 2449331766Sken io->display_name = "abort_task"; 2450331766Sken io->io_to_abort = io_to_abort; 2451331766Sken io->send_abts = TRUE; 2452331766Sken io->scsi_ini_cb = cb; 2453331766Sken io->scsi_ini_cb_arg = arg; 2454331766Sken 2455331766Sken /* now dispatch IO */ 2456331766Sken rc = ocs_scsi_io_dispatch_abort(io, ocs_scsi_abort_io_cb); 2457331766Sken if (rc) { 2458331766Sken scsi_io_printf(io, "Failed to dispatch abort\n"); 2459331766Sken ocs_ref_put(&io->ref); /* ocs_ref_get(): same function */ 2460331766Sken } 2461331766Sken } else { 2462331766Sken io->display_name = "tmf"; 2463331766Sken rc = ocs_scsi_send_io(OCS_HW_IO_INITIATOR_READ, node, io, lun, tmf, NULL, 0, NULL, 2464331766Sken sgl, sgl_count, len, 0, cb, arg); 2465331766Sken } 2466331766Sken 2467331766Sken return rc; 2468331766Sken} 2469331766Sken 2470331766Sken/** 2471331766Sken * @ingroup scsi_api_base 2472331766Sken * @brief Send an FCP IO. 2473331766Sken * 2474331766Sken * @par Description 2475331766Sken * An FCP read/write IO command, with optional task management flags, is sent to @c node. 2476331766Sken * 2477331766Sken * @param type HW IO type to send. 2478331766Sken * @param node Pointer to the node destination of the IO. 2479331766Sken * @param io Pointer to the IO context. 2480331766Sken * @param lun LUN value. 2481331766Sken * @param tmf Task management command. 2482331766Sken * @param cdb Pointer to the SCSI CDB. 2483331766Sken * @param cdb_len Length of the CDB, in bytes. 2484331766Sken * @param dif_info Pointer to the T10 DIF fields, or NULL if no DIF. 2485331766Sken * @param sgl Pointer to the scatter-gather list. 2486331766Sken * @param sgl_count Number of SGL entries in SGL. 2487331766Sken * @param wire_len Payload length, in bytes, of data on wire. 2488331766Sken * @param first_burst Number of first burst bytes to send. 2489331766Sken * @param cb Completion callback. 2490331766Sken * @param arg Application-specified completion callback argument. 2491331766Sken * 2492331766Sken * @return Returns 0 on success, or a negative error code value on failure. 2493331766Sken */ 2494331766Sken 2495331766Sken/* tc: could elminiate LUN, as it's part of the IO structure */ 2496331766Sken 2497331766Skenstatic int32_t ocs_scsi_send_io(ocs_hw_io_type_e type, ocs_node_t *node, ocs_io_t *io, uint64_t lun, 2498331766Sken ocs_scsi_tmf_cmd_e tmf, uint8_t *cdb, uint32_t cdb_len, 2499331766Sken ocs_scsi_dif_info_t *dif_info, 2500331766Sken ocs_scsi_sgl_t *sgl, uint32_t sgl_count, uint32_t wire_len, uint32_t first_burst, 2501331766Sken ocs_scsi_rsp_io_cb_t cb, void *arg) 2502331766Sken{ 2503331766Sken int32_t rc; 2504331766Sken ocs_t *ocs; 2505331766Sken fcp_cmnd_iu_t *cmnd; 2506331766Sken uint32_t cmnd_bytes = 0; 2507331766Sken uint32_t *fcp_dl; 2508331766Sken uint8_t tmf_flags = 0; 2509331766Sken 2510331766Sken ocs_assert(io->node, -1); 2511331766Sken ocs_assert(io->node == node, -1); 2512331766Sken ocs_assert(io, -1); 2513331766Sken ocs = io->ocs; 2514331766Sken ocs_assert(cb, -1); 2515331766Sken 2516331766Sken io->sgl_count = sgl_count; 2517331766Sken 2518331766Sken /* Copy SGL if needed */ 2519331766Sken if (sgl != io->sgl) { 2520331766Sken ocs_assert(sgl_count <= io->sgl_allocated, -1); 2521331766Sken ocs_memcpy(io->sgl, sgl, sizeof(*io->sgl) * sgl_count); 2522331766Sken } 2523331766Sken 2524331766Sken /* save initiator and target task tags for debugging */ 2525331766Sken io->tgt_task_tag = 0xffff; 2526331766Sken 2527331766Sken io->wire_len = wire_len; 2528331766Sken io->hio_type = type; 2529331766Sken 2530331766Sken if (OCS_LOG_ENABLE_SCSI_TRACE(ocs)) { 2531331766Sken char buf[80]; 2532331766Sken ocs_textbuf_t txtbuf; 2533331766Sken uint32_t i; 2534331766Sken 2535331766Sken ocs_textbuf_init(ocs, &txtbuf, buf, sizeof(buf)); 2536331766Sken 2537331766Sken ocs_textbuf_printf(&txtbuf, "cdb%d: ", cdb_len); 2538331766Sken for (i = 0; i < cdb_len; i ++) { 2539331766Sken ocs_textbuf_printf(&txtbuf, "%02X%s", cdb[i], (i == (cdb_len-1)) ? "" : " "); 2540331766Sken } 2541331766Sken scsi_io_printf(io, "%s len %d, %s\n", (io->hio_type == OCS_HW_IO_INITIATOR_READ) ? "read" : 2542331766Sken (io->hio_type == OCS_HW_IO_INITIATOR_WRITE) ? "write" : "", io->wire_len, 2543331766Sken ocs_textbuf_get_buffer(&txtbuf)); 2544331766Sken } 2545331766Sken 2546331766Sken 2547331766Sken ocs_assert(io->cmdbuf.virt, -1); 2548331766Sken 2549331766Sken cmnd = io->cmdbuf.virt; 2550331766Sken 2551331766Sken ocs_assert(sizeof(*cmnd) <= io->cmdbuf.size, -1); 2552331766Sken 2553331766Sken ocs_memset(cmnd, 0, sizeof(*cmnd)); 2554331766Sken 2555331766Sken /* Default FCP_CMND IU doesn't include additional CDB bytes but does include FCP_DL */ 2556331766Sken cmnd_bytes = sizeof(fcp_cmnd_iu_t) - sizeof(cmnd->fcp_cdb_and_dl) + sizeof(uint32_t); 2557331766Sken 2558331766Sken fcp_dl = (uint32_t*)(&(cmnd->fcp_cdb_and_dl)); 2559331766Sken 2560331766Sken if (cdb) { 2561331766Sken if (cdb_len <= 16) { 2562331766Sken ocs_memcpy(cmnd->fcp_cdb, cdb, cdb_len); 2563331766Sken } else { 2564331766Sken uint32_t addl_cdb_bytes; 2565331766Sken 2566331766Sken ocs_memcpy(cmnd->fcp_cdb, cdb, 16); 2567331766Sken addl_cdb_bytes = cdb_len - 16; 2568331766Sken ocs_memcpy(cmnd->fcp_cdb_and_dl, &(cdb[16]), addl_cdb_bytes); 2569331766Sken /* additional_fcp_cdb_length is in words, not bytes */ 2570331766Sken cmnd->additional_fcp_cdb_length = (addl_cdb_bytes + 3) / 4; 2571331766Sken fcp_dl += cmnd->additional_fcp_cdb_length; 2572331766Sken 2573331766Sken /* Round up additional CDB bytes */ 2574331766Sken cmnd_bytes += (addl_cdb_bytes + 3) & ~0x3; 2575331766Sken } 2576331766Sken } 2577331766Sken 2578331766Sken be64enc(cmnd->fcp_lun, CAM_EXTLUN_BYTE_SWIZZLE(lun)); 2579331766Sken 2580331766Sken if (node->fcp2device) { 2581331766Sken if(ocs_get_crn(node, &cmnd->command_reference_number, 2582331766Sken lun)) { 2583331766Sken return -1; 2584331766Sken } 2585331766Sken } 2586331766Sken 2587331766Sken switch (tmf) { 2588331766Sken case OCS_SCSI_TMF_QUERY_TASK_SET: 2589331766Sken tmf_flags = FCP_QUERY_TASK_SET; 2590331766Sken break; 2591331766Sken case OCS_SCSI_TMF_ABORT_TASK_SET: 2592331766Sken tmf_flags = FCP_ABORT_TASK_SET; 2593331766Sken break; 2594331766Sken case OCS_SCSI_TMF_CLEAR_TASK_SET: 2595331766Sken tmf_flags = FCP_CLEAR_TASK_SET; 2596331766Sken break; 2597331766Sken case OCS_SCSI_TMF_QUERY_ASYNCHRONOUS_EVENT: 2598331766Sken tmf_flags = FCP_QUERY_ASYNCHRONOUS_EVENT; 2599331766Sken break; 2600331766Sken case OCS_SCSI_TMF_LOGICAL_UNIT_RESET: 2601331766Sken tmf_flags = FCP_LOGICAL_UNIT_RESET; 2602331766Sken break; 2603331766Sken case OCS_SCSI_TMF_CLEAR_ACA: 2604331766Sken tmf_flags = FCP_CLEAR_ACA; 2605331766Sken break; 2606331766Sken case OCS_SCSI_TMF_TARGET_RESET: 2607331766Sken tmf_flags = FCP_TARGET_RESET; 2608331766Sken break; 2609331766Sken default: 2610331766Sken tmf_flags = 0; 2611331766Sken } 2612331766Sken cmnd->task_management_flags = tmf_flags; 2613331766Sken 2614331766Sken *fcp_dl = ocs_htobe32(io->wire_len); 2615331766Sken 2616331766Sken switch (io->hio_type) { 2617331766Sken case OCS_HW_IO_INITIATOR_READ: 2618331766Sken cmnd->rddata = 1; 2619331766Sken break; 2620331766Sken case OCS_HW_IO_INITIATOR_WRITE: 2621331766Sken cmnd->wrdata = 1; 2622331766Sken break; 2623331766Sken case OCS_HW_IO_INITIATOR_NODATA: 2624331766Sken /* sets neither */ 2625331766Sken break; 2626331766Sken default: 2627331766Sken ocs_log_test(ocs, "bad IO type %d\n", io->hio_type); 2628331766Sken return -1; 2629331766Sken } 2630331766Sken 2631331766Sken rc = ocs_scsi_convert_dif_info(ocs, dif_info, &io->hw_dif); 2632331766Sken if (rc) { 2633331766Sken return rc; 2634331766Sken } 2635331766Sken 2636331766Sken io->scsi_ini_cb = cb; 2637331766Sken io->scsi_ini_cb_arg = arg; 2638331766Sken 2639331766Sken /* set command and response buffers in the iparam */ 2640331766Sken io->iparam.fcp_ini.cmnd = &io->cmdbuf; 2641331766Sken io->iparam.fcp_ini.cmnd_size = cmnd_bytes; 2642331766Sken io->iparam.fcp_ini.rsp = &io->rspbuf; 2643331766Sken io->iparam.fcp_ini.flags = 0; 2644331766Sken io->iparam.fcp_ini.dif_oper = io->hw_dif.dif; 2645331766Sken io->iparam.fcp_ini.blk_size = io->hw_dif.blk_size; 2646331766Sken io->iparam.fcp_ini.timeout = io->timeout; 2647331766Sken io->iparam.fcp_ini.first_burst = first_burst; 2648331766Sken 2649331766Sken return ocs_scsi_io_dispatch(io, ocs_initiator_io_cb); 2650331766Sken} 2651331766Sken 2652331766Sken/** 2653331766Sken * @ingroup scsi_api_base 2654331766Sken * @brief Callback for an aborted IO. 2655331766Sken * 2656331766Sken * @par Description 2657331766Sken * Callback function invoked upon completion of an IO abort request. 2658331766Sken * 2659331766Sken * @param hio HW IO context. 2660331766Sken * @param rnode Remote node. 2661331766Sken * @param len Response length. 2662331766Sken * @param status Completion status. 2663331766Sken * @param ext_status Extended completion status. 2664331766Sken * @param arg Application-specific callback, usually IO context. 2665331766Sken 2666331766Sken * @return Returns 0 on success, or a negative error code value on failure. 2667331766Sken */ 2668331766Sken 2669331766Skenstatic int32_t 2670331766Skenocs_scsi_abort_io_cb(struct ocs_hw_io_s *hio, ocs_remote_node_t *rnode, uint32_t len, int32_t status, 2671331766Sken uint32_t ext_status, void *arg) 2672331766Sken{ 2673331766Sken ocs_io_t *io = arg; 2674331766Sken ocs_t *ocs; 2675331766Sken ocs_scsi_io_status_e scsi_status = OCS_SCSI_STATUS_GOOD; 2676331766Sken 2677331766Sken ocs_assert(io, -1); 2678331766Sken ocs_assert(ocs_io_busy(io), -1); 2679331766Sken ocs_assert(io->ocs, -1); 2680331766Sken ocs_assert(io->io_to_abort, -1); 2681331766Sken ocs = io->ocs; 2682331766Sken 2683331766Sken ocs_log_debug(ocs, "status %d ext %d\n", status, ext_status); 2684331766Sken 2685331766Sken /* done with IO to abort */ 2686331766Sken ocs_ref_put(&io->io_to_abort->ref); /* ocs_ref_get(): ocs_scsi_send_tmf() */ 2687331766Sken 2688331766Sken ocs_scsi_io_free_ovfl(io); 2689331766Sken 2690331766Sken switch (status) { 2691331766Sken case SLI4_FC_WCQE_STATUS_SUCCESS: 2692331766Sken scsi_status = OCS_SCSI_STATUS_GOOD; 2693331766Sken break; 2694331766Sken case SLI4_FC_WCQE_STATUS_LOCAL_REJECT: 2695331766Sken if (ext_status == SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED) { 2696331766Sken scsi_status = OCS_SCSI_STATUS_ABORTED; 2697331766Sken } else if (ext_status == SLI4_FC_LOCAL_REJECT_NO_XRI) { 2698331766Sken scsi_status = OCS_SCSI_STATUS_NO_IO; 2699331766Sken } else if (ext_status == SLI4_FC_LOCAL_REJECT_ABORT_IN_PROGRESS) { 2700331766Sken scsi_status = OCS_SCSI_STATUS_ABORT_IN_PROGRESS; 2701331766Sken } else { 2702331766Sken ocs_log_test(ocs, "Unhandled local reject 0x%x/0x%x\n", status, ext_status); 2703331766Sken scsi_status = OCS_SCSI_STATUS_ERROR; 2704331766Sken } 2705331766Sken break; 2706331766Sken default: 2707331766Sken scsi_status = OCS_SCSI_STATUS_ERROR; 2708331766Sken break; 2709331766Sken } 2710331766Sken 2711331766Sken if (io->scsi_ini_cb) { 2712331766Sken (*io->scsi_ini_cb)(io, scsi_status, NULL, 0, io->scsi_ini_cb_arg); 2713331766Sken } else { 2714331766Sken ocs_scsi_io_free(io); 2715331766Sken } 2716331766Sken 2717331766Sken ocs_scsi_check_pending(ocs); 2718331766Sken return 0; 2719331766Sken} 2720331766Sken 2721331766Sken/** 2722331766Sken * @ingroup scsi_api_base 2723331766Sken * @brief Return SCSI API integer valued property. 2724331766Sken * 2725331766Sken * @par Description 2726331766Sken * This function is called by a target-server or initiator-client to 2727331766Sken * retrieve an integer valued property. 2728331766Sken * 2729331766Sken * @param ocs Pointer to the ocs. 2730331766Sken * @param prop Property value to return. 2731331766Sken * 2732331766Sken * @return Returns a value, or 0 if invalid property was requested. 2733331766Sken */ 2734331766Skenuint32_t 2735331766Skenocs_scsi_get_property(ocs_t *ocs, ocs_scsi_property_e prop) 2736331766Sken{ 2737331766Sken ocs_xport_t *xport = ocs->xport; 2738331766Sken uint32_t val; 2739331766Sken 2740331766Sken switch (prop) { 2741331766Sken case OCS_SCSI_MAX_SGE: 2742331766Sken if (0 == ocs_hw_get(&ocs->hw, OCS_HW_MAX_SGE, &val)) { 2743331766Sken return val; 2744331766Sken } 2745331766Sken break; 2746331766Sken case OCS_SCSI_MAX_SGL: 2747331766Sken if (ocs->ctrlmask & OCS_CTRLMASK_TEST_CHAINED_SGLS) { 2748331766Sken /* 2749331766Sken * If chain SGL test-mode is enabled, the number of HW SGEs 2750331766Sken * has been limited; report back original max. 2751331766Sken */ 2752331766Sken return (OCS_FC_MAX_SGL); 2753331766Sken } 2754331766Sken if (0 == ocs_hw_get(&ocs->hw, OCS_HW_N_SGL, &val)) { 2755331766Sken return val; 2756331766Sken } 2757331766Sken break; 2758331766Sken case OCS_SCSI_MAX_IOS: 2759331766Sken return ocs_io_pool_allocated(xport->io_pool); 2760331766Sken case OCS_SCSI_DIF_CAPABLE: 2761331766Sken if (0 == ocs_hw_get(&ocs->hw, OCS_HW_DIF_CAPABLE, &val)) { 2762331766Sken return val; 2763331766Sken } 2764331766Sken break; 2765331766Sken case OCS_SCSI_MAX_FIRST_BURST: 2766331766Sken return 0; 2767331766Sken case OCS_SCSI_DIF_MULTI_SEPARATE: 2768331766Sken if (ocs_hw_get(&ocs->hw, OCS_HW_DIF_MULTI_SEPARATE, &val) == 0) { 2769331766Sken return val; 2770331766Sken } 2771331766Sken break; 2772331766Sken case OCS_SCSI_ENABLE_TASK_SET_FULL: 2773331766Sken /* Return FALSE if we are send frame capable */ 2774331766Sken if (ocs_hw_get(&ocs->hw, OCS_HW_SEND_FRAME_CAPABLE, &val) == 0) { 2775331766Sken return ! val; 2776331766Sken } 2777331766Sken break; 2778331766Sken default: 2779331766Sken break; 2780331766Sken } 2781331766Sken 2782331766Sken ocs_log_debug(ocs, "invalid property request %d\n", prop); 2783331766Sken return 0; 2784331766Sken} 2785331766Sken 2786331766Sken/** 2787331766Sken * @ingroup scsi_api_base 2788331766Sken * @brief Return a property pointer. 2789331766Sken * 2790331766Sken * @par Description 2791331766Sken * This function is called by a target-server or initiator-client to 2792331766Sken * retrieve a pointer to the requested property. 2793331766Sken * 2794331766Sken * @param ocs Pointer to the ocs. 2795331766Sken * @param prop Property value to return. 2796331766Sken * 2797331766Sken * @return Returns pointer to the requested property, or NULL otherwise. 2798331766Sken */ 2799331766Skenvoid *ocs_scsi_get_property_ptr(ocs_t *ocs, ocs_scsi_property_e prop) 2800331766Sken{ 2801331766Sken void *rc = NULL; 2802331766Sken 2803331766Sken switch (prop) { 2804331766Sken case OCS_SCSI_WWNN: 2805331766Sken rc = ocs_hw_get_ptr(&ocs->hw, OCS_HW_WWN_NODE); 2806331766Sken break; 2807331766Sken case OCS_SCSI_WWPN: 2808331766Sken rc = ocs_hw_get_ptr(&ocs->hw, OCS_HW_WWN_PORT); 2809331766Sken break; 2810331766Sken case OCS_SCSI_PORTNUM: 2811331766Sken rc = ocs_hw_get_ptr(&ocs->hw, OCS_HW_PORTNUM); 2812331766Sken break; 2813331766Sken case OCS_SCSI_BIOS_VERSION_STRING: 2814331766Sken rc = ocs_hw_get_ptr(&ocs->hw, OCS_HW_BIOS_VERSION_STRING); 2815331766Sken break; 2816331766Sken#if defined(OCS_ENABLE_VPD_SUPPORT) 2817331766Sken case OCS_SCSI_SERIALNUMBER: 2818331766Sken { 2819331766Sken uint8_t *pvpd; 2820331766Sken uint32_t vpd_len; 2821331766Sken 2822331766Sken if (ocs_hw_get(&ocs->hw, OCS_HW_VPD_LEN, &vpd_len)) { 2823331766Sken ocs_log_test(ocs, "Can't get VPD length\n"); 2824331766Sken rc = "\012sn-unknown"; 2825331766Sken break; 2826331766Sken } 2827331766Sken 2828331766Sken pvpd = ocs_hw_get_ptr(&ocs->hw, OCS_HW_VPD); 2829331766Sken if (pvpd) { 2830331766Sken rc = ocs_find_vpd(pvpd, vpd_len, "SN"); 2831331766Sken } 2832331766Sken 2833331766Sken if (rc == NULL || 2834331766Sken ocs_strlen(rc) == 0) { 2835331766Sken /* Note: VPD is missing, using wwnn for serial number */ 2836331766Sken scsi_log(ocs, "Note: VPD is missing, using wwnn for serial number\n"); 2837331766Sken /* Use the last 32 bits of the WWN */ 2838331766Sken if ((ocs == NULL) || (ocs->domain == NULL) || (ocs->domain->sport == NULL)) { 2839331766Sken rc = "\011(Unknown)"; 2840331766Sken } else { 2841331766Sken rc = &ocs->domain->sport->wwnn_str[8]; 2842331766Sken } 2843331766Sken } 2844331766Sken break; 2845331766Sken } 2846331766Sken case OCS_SCSI_PARTNUMBER: 2847331766Sken { 2848331766Sken uint8_t *pvpd; 2849331766Sken uint32_t vpd_len; 2850331766Sken 2851331766Sken if (ocs_hw_get(&ocs->hw, OCS_HW_VPD_LEN, &vpd_len)) { 2852331766Sken ocs_log_test(ocs, "Can't get VPD length\n"); 2853331766Sken rc = "\012pn-unknown"; 2854331766Sken break; 2855331766Sken } 2856331766Sken pvpd = ocs_hw_get_ptr(&ocs->hw, OCS_HW_VPD); 2857331766Sken if (pvpd) { 2858331766Sken rc = ocs_find_vpd(pvpd, vpd_len, "PN"); 2859331766Sken if (rc == NULL) { 2860331766Sken rc = "\012pn-unknown"; 2861331766Sken } 2862331766Sken } else { 2863331766Sken rc = "\012pn-unknown"; 2864331766Sken } 2865331766Sken break; 2866331766Sken } 2867331766Sken#endif 2868331766Sken default: 2869331766Sken break; 2870331766Sken } 2871331766Sken 2872331766Sken if (rc == NULL) { 2873331766Sken ocs_log_debug(ocs, "invalid property request %d\n", prop); 2874331766Sken } 2875331766Sken return rc; 2876331766Sken} 2877331766Sken 2878331766Sken/** 2879331766Sken * @ingroup scsi_api_base 2880331766Sken * @brief Notify that delete initiator is complete. 2881331766Sken * 2882331766Sken * @par Description 2883331766Sken * Sent by the target-server to notify the base driver that the work started from 2884331766Sken * ocs_scsi_del_initiator() is now complete and that it is safe for the node to 2885331766Sken * release the rest of its resources. 2886331766Sken * 2887331766Sken * @param node Pointer to the node. 2888331766Sken * 2889331766Sken * @return None. 2890331766Sken */ 2891331766Skenvoid 2892331766Skenocs_scsi_del_initiator_complete(ocs_node_t *node) 2893331766Sken{ 2894331766Sken /* Notify the node to resume */ 2895331766Sken ocs_node_post_event(node, OCS_EVT_NODE_DEL_INI_COMPLETE, NULL); 2896331766Sken} 2897331766Sken 2898331766Sken 2899331766Sken/** 2900331766Sken * @ingroup scsi_api_base 2901331766Sken * @brief Notify that delete target is complete. 2902331766Sken * 2903331766Sken * @par Description 2904331766Sken * Sent by the initiator-client to notify the base driver that the work started from 2905331766Sken * ocs_scsi_del_target() is now complete and that it is safe for the node to 2906331766Sken * release the rest of its resources. 2907331766Sken * 2908331766Sken * @param node Pointer to the node. 2909331766Sken * 2910331766Sken * @return None. 2911331766Sken */ 2912331766Skenvoid 2913331766Skenocs_scsi_del_target_complete(ocs_node_t *node) 2914331766Sken{ 2915331766Sken /* Notify the node to resume */ 2916331766Sken ocs_node_post_event(node, OCS_EVT_NODE_DEL_TGT_COMPLETE, NULL); 2917331766Sken} 2918331766Sken 2919331766Sken 2920331766Sken/** 2921331766Sken * @brief Update transferred count 2922331766Sken * 2923331766Sken * @par Description 2924331766Sken * Updates io->transferred, as required when using first burst, when the amount 2925331766Sken * of first burst data processed differs from the amount of first burst 2926331766Sken * data received. 2927331766Sken * 2928331766Sken * @param io Pointer to the io object. 2929331766Sken * @param transferred Number of bytes transferred out of first burst buffers. 2930331766Sken * 2931331766Sken * @return None. 2932331766Sken */ 2933331766Skenvoid 2934331766Skenocs_scsi_update_first_burst_transferred(ocs_io_t *io, uint32_t transferred) 2935331766Sken{ 2936331766Sken io->transferred = transferred; 2937331766Sken} 2938331766Sken 2939331766Sken/** 2940331766Sken * @brief Register bounce callback for multi-threading. 2941331766Sken * 2942331766Sken * @par Description 2943331766Sken * Register the back end bounce function. 2944331766Sken * 2945331766Sken * @param ocs Pointer to device object. 2946331766Sken * @param fctn Function pointer of bounce function. 2947331766Sken * 2948331766Sken * @return None. 2949331766Sken */ 2950331766Skenvoid 2951331766Skenocs_scsi_register_bounce(ocs_t *ocs, void(*fctn)(void(*fctn)(void *arg), void *arg, uint32_t s_id, uint32_t d_id, 2952331766Sken uint32_t ox_id)) 2953331766Sken{ 2954331766Sken ocs_hw_rtn_e rc; 2955331766Sken 2956331766Sken rc = ocs_hw_callback(&ocs->hw, OCS_HW_CB_BOUNCE, fctn, NULL); 2957331766Sken if (rc) { 2958331766Sken ocs_log_test(ocs, "ocs_hw_callback(OCS_HW_CB_BOUNCE) failed: %d\n", rc); 2959331766Sken } 2960331766Sken} 2961