18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is part of the Chelsio FCoE driver for Linux.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
78c2ecf20Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
88c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
98c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the
108c2ecf20Sopenharmony_ci * OpenIB.org BSD license below:
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
138c2ecf20Sopenharmony_ci *     without modification, are permitted provided that the following
148c2ecf20Sopenharmony_ci *     conditions are met:
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci *      - Redistributions of source code must retain the above
178c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
188c2ecf20Sopenharmony_ci *        disclaimer.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
218c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
228c2ecf20Sopenharmony_ci *        disclaimer in the documentation and/or other materials
238c2ecf20Sopenharmony_ci *        provided with the distribution.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
268c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
278c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
288c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
298c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
308c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
318c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
328c2ecf20Sopenharmony_ci * SOFTWARE.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <linux/device.h>
368c2ecf20Sopenharmony_ci#include <linux/delay.h>
378c2ecf20Sopenharmony_ci#include <linux/ctype.h>
388c2ecf20Sopenharmony_ci#include <linux/kernel.h>
398c2ecf20Sopenharmony_ci#include <linux/slab.h>
408c2ecf20Sopenharmony_ci#include <linux/string.h>
418c2ecf20Sopenharmony_ci#include <linux/compiler.h>
428c2ecf20Sopenharmony_ci#include <linux/export.h>
438c2ecf20Sopenharmony_ci#include <linux/module.h>
448c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
458c2ecf20Sopenharmony_ci#include <asm/page.h>
468c2ecf20Sopenharmony_ci#include <scsi/scsi.h>
478c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h>
488c2ecf20Sopenharmony_ci#include <scsi/scsi_transport_fc.h>
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#include "csio_hw.h"
518c2ecf20Sopenharmony_ci#include "csio_lnode.h"
528c2ecf20Sopenharmony_ci#include "csio_rnode.h"
538c2ecf20Sopenharmony_ci#include "csio_scsi.h"
548c2ecf20Sopenharmony_ci#include "csio_init.h"
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ciint csio_scsi_eqsize = 65536;
578c2ecf20Sopenharmony_ciint csio_scsi_iqlen = 128;
588c2ecf20Sopenharmony_ciint csio_scsi_ioreqs = 2048;
598c2ecf20Sopenharmony_ciuint32_t csio_max_scan_tmo;
608c2ecf20Sopenharmony_ciuint32_t csio_delta_scan_tmo = 5;
618c2ecf20Sopenharmony_ciint csio_lun_qdepth = 32;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int csio_ddp_descs = 128;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int csio_do_abrt_cls(struct csio_hw *,
668c2ecf20Sopenharmony_ci				      struct csio_ioreq *, bool);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic void csio_scsis_uninit(struct csio_ioreq *, enum csio_scsi_ev);
698c2ecf20Sopenharmony_cistatic void csio_scsis_io_active(struct csio_ioreq *, enum csio_scsi_ev);
708c2ecf20Sopenharmony_cistatic void csio_scsis_tm_active(struct csio_ioreq *, enum csio_scsi_ev);
718c2ecf20Sopenharmony_cistatic void csio_scsis_aborting(struct csio_ioreq *, enum csio_scsi_ev);
728c2ecf20Sopenharmony_cistatic void csio_scsis_closing(struct csio_ioreq *, enum csio_scsi_ev);
738c2ecf20Sopenharmony_cistatic void csio_scsis_shost_cmpl_await(struct csio_ioreq *, enum csio_scsi_ev);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/*
768c2ecf20Sopenharmony_ci * csio_scsi_match_io - Match an ioreq with the given SCSI level data.
778c2ecf20Sopenharmony_ci * @ioreq: The I/O request
788c2ecf20Sopenharmony_ci * @sld: Level information
798c2ecf20Sopenharmony_ci *
808c2ecf20Sopenharmony_ci * Should be called with lock held.
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci */
838c2ecf20Sopenharmony_cistatic bool
848c2ecf20Sopenharmony_cicsio_scsi_match_io(struct csio_ioreq *ioreq, struct csio_scsi_level_data *sld)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmnd = csio_scsi_cmnd(ioreq);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	switch (sld->level) {
898c2ecf20Sopenharmony_ci	case CSIO_LEV_LUN:
908c2ecf20Sopenharmony_ci		if (scmnd == NULL)
918c2ecf20Sopenharmony_ci			return false;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		return ((ioreq->lnode == sld->lnode) &&
948c2ecf20Sopenharmony_ci			(ioreq->rnode == sld->rnode) &&
958c2ecf20Sopenharmony_ci			((uint64_t)scmnd->device->lun == sld->oslun));
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	case CSIO_LEV_RNODE:
988c2ecf20Sopenharmony_ci		return ((ioreq->lnode == sld->lnode) &&
998c2ecf20Sopenharmony_ci				(ioreq->rnode == sld->rnode));
1008c2ecf20Sopenharmony_ci	case CSIO_LEV_LNODE:
1018c2ecf20Sopenharmony_ci		return (ioreq->lnode == sld->lnode);
1028c2ecf20Sopenharmony_ci	case CSIO_LEV_ALL:
1038c2ecf20Sopenharmony_ci		return true;
1048c2ecf20Sopenharmony_ci	default:
1058c2ecf20Sopenharmony_ci		return false;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * csio_scsi_gather_active_ios - Gather active I/Os based on level
1118c2ecf20Sopenharmony_ci * @scm: SCSI module
1128c2ecf20Sopenharmony_ci * @sld: Level information
1138c2ecf20Sopenharmony_ci * @dest: The queue where these I/Os have to be gathered.
1148c2ecf20Sopenharmony_ci *
1158c2ecf20Sopenharmony_ci * Should be called with lock held.
1168c2ecf20Sopenharmony_ci */
1178c2ecf20Sopenharmony_cistatic void
1188c2ecf20Sopenharmony_cicsio_scsi_gather_active_ios(struct csio_scsim *scm,
1198c2ecf20Sopenharmony_ci			    struct csio_scsi_level_data *sld,
1208c2ecf20Sopenharmony_ci			    struct list_head *dest)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct list_head *tmp, *next;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (list_empty(&scm->active_q))
1258c2ecf20Sopenharmony_ci		return;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* Just splice the entire active_q into dest */
1288c2ecf20Sopenharmony_ci	if (sld->level == CSIO_LEV_ALL) {
1298c2ecf20Sopenharmony_ci		list_splice_tail_init(&scm->active_q, dest);
1308c2ecf20Sopenharmony_ci		return;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	list_for_each_safe(tmp, next, &scm->active_q) {
1348c2ecf20Sopenharmony_ci		if (csio_scsi_match_io((struct csio_ioreq *)tmp, sld)) {
1358c2ecf20Sopenharmony_ci			list_del_init(tmp);
1368c2ecf20Sopenharmony_ci			list_add_tail(tmp, dest);
1378c2ecf20Sopenharmony_ci		}
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic inline bool
1428c2ecf20Sopenharmony_cicsio_scsi_itnexus_loss_error(uint16_t error)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	switch (error) {
1458c2ecf20Sopenharmony_ci	case FW_ERR_LINK_DOWN:
1468c2ecf20Sopenharmony_ci	case FW_RDEV_NOT_READY:
1478c2ecf20Sopenharmony_ci	case FW_ERR_RDEV_LOST:
1488c2ecf20Sopenharmony_ci	case FW_ERR_RDEV_LOGO:
1498c2ecf20Sopenharmony_ci	case FW_ERR_RDEV_IMPL_LOGO:
1508c2ecf20Sopenharmony_ci		return 1;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/*
1568c2ecf20Sopenharmony_ci * csio_scsi_fcp_cmnd - Frame the SCSI FCP command paylod.
1578c2ecf20Sopenharmony_ci * @req: IO req structure.
1588c2ecf20Sopenharmony_ci * @addr: DMA location to place the payload.
1598c2ecf20Sopenharmony_ci *
1608c2ecf20Sopenharmony_ci * This routine is shared between FCP_WRITE, FCP_READ and FCP_CMD requests.
1618c2ecf20Sopenharmony_ci */
1628c2ecf20Sopenharmony_cistatic inline void
1638c2ecf20Sopenharmony_cicsio_scsi_fcp_cmnd(struct csio_ioreq *req, void *addr)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct fcp_cmnd *fcp_cmnd = (struct fcp_cmnd *)addr;
1668c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	/* Check for Task Management */
1698c2ecf20Sopenharmony_ci	if (likely(scmnd->SCp.Message == 0)) {
1708c2ecf20Sopenharmony_ci		int_to_scsilun(scmnd->device->lun, &fcp_cmnd->fc_lun);
1718c2ecf20Sopenharmony_ci		fcp_cmnd->fc_tm_flags = 0;
1728c2ecf20Sopenharmony_ci		fcp_cmnd->fc_cmdref = 0;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci		memcpy(fcp_cmnd->fc_cdb, scmnd->cmnd, 16);
1758c2ecf20Sopenharmony_ci		fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE;
1768c2ecf20Sopenharmony_ci		fcp_cmnd->fc_dl = cpu_to_be32(scsi_bufflen(scmnd));
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		if (req->nsge)
1798c2ecf20Sopenharmony_ci			if (req->datadir == DMA_TO_DEVICE)
1808c2ecf20Sopenharmony_ci				fcp_cmnd->fc_flags = FCP_CFL_WRDATA;
1818c2ecf20Sopenharmony_ci			else
1828c2ecf20Sopenharmony_ci				fcp_cmnd->fc_flags = FCP_CFL_RDDATA;
1838c2ecf20Sopenharmony_ci		else
1848c2ecf20Sopenharmony_ci			fcp_cmnd->fc_flags = 0;
1858c2ecf20Sopenharmony_ci	} else {
1868c2ecf20Sopenharmony_ci		memset(fcp_cmnd, 0, sizeof(*fcp_cmnd));
1878c2ecf20Sopenharmony_ci		int_to_scsilun(scmnd->device->lun, &fcp_cmnd->fc_lun);
1888c2ecf20Sopenharmony_ci		fcp_cmnd->fc_tm_flags = (uint8_t)scmnd->SCp.Message;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/*
1938c2ecf20Sopenharmony_ci * csio_scsi_init_cmd_wr - Initialize the SCSI CMD WR.
1948c2ecf20Sopenharmony_ci * @req: IO req structure.
1958c2ecf20Sopenharmony_ci * @addr: DMA location to place the payload.
1968c2ecf20Sopenharmony_ci * @size: Size of WR (including FW WR + immed data + rsp SG entry
1978c2ecf20Sopenharmony_ci *
1988c2ecf20Sopenharmony_ci * Wrapper for populating fw_scsi_cmd_wr.
1998c2ecf20Sopenharmony_ci */
2008c2ecf20Sopenharmony_cistatic inline void
2018c2ecf20Sopenharmony_cicsio_scsi_init_cmd_wr(struct csio_ioreq *req, void *addr, uint32_t size)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
2048c2ecf20Sopenharmony_ci	struct csio_rnode *rn = req->rnode;
2058c2ecf20Sopenharmony_ci	struct fw_scsi_cmd_wr *wr = (struct fw_scsi_cmd_wr *)addr;
2068c2ecf20Sopenharmony_ci	struct csio_dma_buf *dma_buf;
2078c2ecf20Sopenharmony_ci	uint8_t imm = csio_hw_to_scsim(hw)->proto_cmd_len;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	wr->op_immdlen = cpu_to_be32(FW_WR_OP_V(FW_SCSI_CMD_WR) |
2108c2ecf20Sopenharmony_ci					  FW_SCSI_CMD_WR_IMMDLEN(imm));
2118c2ecf20Sopenharmony_ci	wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID_V(rn->flowid) |
2128c2ecf20Sopenharmony_ci					    FW_WR_LEN16_V(
2138c2ecf20Sopenharmony_ci						DIV_ROUND_UP(size, 16)));
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	wr->cookie = (uintptr_t) req;
2168c2ecf20Sopenharmony_ci	wr->iqid = cpu_to_be16(csio_q_physiqid(hw, req->iq_idx));
2178c2ecf20Sopenharmony_ci	wr->tmo_val = (uint8_t) req->tmo;
2188c2ecf20Sopenharmony_ci	wr->r3 = 0;
2198c2ecf20Sopenharmony_ci	memset(&wr->r5, 0, 8);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* Get RSP DMA buffer */
2228c2ecf20Sopenharmony_ci	dma_buf = &req->dma_buf;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* Prepare RSP SGL */
2258c2ecf20Sopenharmony_ci	wr->rsp_dmalen = cpu_to_be32(dma_buf->len);
2268c2ecf20Sopenharmony_ci	wr->rsp_dmaaddr = cpu_to_be64(dma_buf->paddr);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	wr->r6 = 0;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	wr->u.fcoe.ctl_pri = 0;
2318c2ecf20Sopenharmony_ci	wr->u.fcoe.cp_en_class = 0;
2328c2ecf20Sopenharmony_ci	wr->u.fcoe.r4_lo[0] = 0;
2338c2ecf20Sopenharmony_ci	wr->u.fcoe.r4_lo[1] = 0;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* Frame a FCP command */
2368c2ecf20Sopenharmony_ci	csio_scsi_fcp_cmnd(req, (void *)((uintptr_t)addr +
2378c2ecf20Sopenharmony_ci				    sizeof(struct fw_scsi_cmd_wr)));
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci#define CSIO_SCSI_CMD_WR_SZ(_imm)					\
2418c2ecf20Sopenharmony_ci	(sizeof(struct fw_scsi_cmd_wr) +		/* WR size */	\
2428c2ecf20Sopenharmony_ci	 ALIGN((_imm), 16))				/* Immed data */
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci#define CSIO_SCSI_CMD_WR_SZ_16(_imm)					\
2458c2ecf20Sopenharmony_ci			(ALIGN(CSIO_SCSI_CMD_WR_SZ((_imm)), 16))
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci/*
2488c2ecf20Sopenharmony_ci * csio_scsi_cmd - Create a SCSI CMD WR.
2498c2ecf20Sopenharmony_ci * @req: IO req structure.
2508c2ecf20Sopenharmony_ci *
2518c2ecf20Sopenharmony_ci * Gets a WR slot in the ingress queue and initializes it with SCSI CMD WR.
2528c2ecf20Sopenharmony_ci *
2538c2ecf20Sopenharmony_ci */
2548c2ecf20Sopenharmony_cistatic inline void
2558c2ecf20Sopenharmony_cicsio_scsi_cmd(struct csio_ioreq *req)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct csio_wr_pair wrp;
2588c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
2598c2ecf20Sopenharmony_ci	struct csio_scsim *scsim = csio_hw_to_scsim(hw);
2608c2ecf20Sopenharmony_ci	uint32_t size = CSIO_SCSI_CMD_WR_SZ_16(scsim->proto_cmd_len);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp);
2638c2ecf20Sopenharmony_ci	if (unlikely(req->drv_status != 0))
2648c2ecf20Sopenharmony_ci		return;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (wrp.size1 >= size) {
2678c2ecf20Sopenharmony_ci		/* Initialize WR in one shot */
2688c2ecf20Sopenharmony_ci		csio_scsi_init_cmd_wr(req, wrp.addr1, size);
2698c2ecf20Sopenharmony_ci	} else {
2708c2ecf20Sopenharmony_ci		uint8_t *tmpwr = csio_q_eq_wrap(hw, req->eq_idx);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		/*
2738c2ecf20Sopenharmony_ci		 * Make a temporary copy of the WR and write back
2748c2ecf20Sopenharmony_ci		 * the copy into the WR pair.
2758c2ecf20Sopenharmony_ci		 */
2768c2ecf20Sopenharmony_ci		csio_scsi_init_cmd_wr(req, (void *)tmpwr, size);
2778c2ecf20Sopenharmony_ci		memcpy(wrp.addr1, tmpwr, wrp.size1);
2788c2ecf20Sopenharmony_ci		memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1);
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci/*
2838c2ecf20Sopenharmony_ci * csio_scsi_init_ulptx_dsgl - Fill in a ULP_TX_SC_DSGL
2848c2ecf20Sopenharmony_ci * @hw: HW module
2858c2ecf20Sopenharmony_ci * @req: IO request
2868c2ecf20Sopenharmony_ci * @sgl: ULP TX SGL pointer.
2878c2ecf20Sopenharmony_ci *
2888c2ecf20Sopenharmony_ci */
2898c2ecf20Sopenharmony_cistatic inline void
2908c2ecf20Sopenharmony_cicsio_scsi_init_ultptx_dsgl(struct csio_hw *hw, struct csio_ioreq *req,
2918c2ecf20Sopenharmony_ci			   struct ulptx_sgl *sgl)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct ulptx_sge_pair *sge_pair = NULL;
2948c2ecf20Sopenharmony_ci	struct scatterlist *sgel;
2958c2ecf20Sopenharmony_ci	uint32_t i = 0;
2968c2ecf20Sopenharmony_ci	uint32_t xfer_len;
2978c2ecf20Sopenharmony_ci	struct list_head *tmp;
2988c2ecf20Sopenharmony_ci	struct csio_dma_buf *dma_buf;
2998c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	sgl->cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | ULPTX_MORE_F |
3028c2ecf20Sopenharmony_ci				     ULPTX_NSGE_V(req->nsge));
3038c2ecf20Sopenharmony_ci	/* Now add the data SGLs */
3048c2ecf20Sopenharmony_ci	if (likely(!req->dcopy)) {
3058c2ecf20Sopenharmony_ci		scsi_for_each_sg(scmnd, sgel, req->nsge, i) {
3068c2ecf20Sopenharmony_ci			if (i == 0) {
3078c2ecf20Sopenharmony_ci				sgl->addr0 = cpu_to_be64(sg_dma_address(sgel));
3088c2ecf20Sopenharmony_ci				sgl->len0 = cpu_to_be32(sg_dma_len(sgel));
3098c2ecf20Sopenharmony_ci				sge_pair = (struct ulptx_sge_pair *)(sgl + 1);
3108c2ecf20Sopenharmony_ci				continue;
3118c2ecf20Sopenharmony_ci			}
3128c2ecf20Sopenharmony_ci			if ((i - 1) & 0x1) {
3138c2ecf20Sopenharmony_ci				sge_pair->addr[1] = cpu_to_be64(
3148c2ecf20Sopenharmony_ci							sg_dma_address(sgel));
3158c2ecf20Sopenharmony_ci				sge_pair->len[1] = cpu_to_be32(
3168c2ecf20Sopenharmony_ci							sg_dma_len(sgel));
3178c2ecf20Sopenharmony_ci				sge_pair++;
3188c2ecf20Sopenharmony_ci			} else {
3198c2ecf20Sopenharmony_ci				sge_pair->addr[0] = cpu_to_be64(
3208c2ecf20Sopenharmony_ci							sg_dma_address(sgel));
3218c2ecf20Sopenharmony_ci				sge_pair->len[0] = cpu_to_be32(
3228c2ecf20Sopenharmony_ci							sg_dma_len(sgel));
3238c2ecf20Sopenharmony_ci			}
3248c2ecf20Sopenharmony_ci		}
3258c2ecf20Sopenharmony_ci	} else {
3268c2ecf20Sopenharmony_ci		/* Program sg elements with driver's DDP buffer */
3278c2ecf20Sopenharmony_ci		xfer_len = scsi_bufflen(scmnd);
3288c2ecf20Sopenharmony_ci		list_for_each(tmp, &req->gen_list) {
3298c2ecf20Sopenharmony_ci			dma_buf = (struct csio_dma_buf *)tmp;
3308c2ecf20Sopenharmony_ci			if (i == 0) {
3318c2ecf20Sopenharmony_ci				sgl->addr0 = cpu_to_be64(dma_buf->paddr);
3328c2ecf20Sopenharmony_ci				sgl->len0 = cpu_to_be32(
3338c2ecf20Sopenharmony_ci						min(xfer_len, dma_buf->len));
3348c2ecf20Sopenharmony_ci				sge_pair = (struct ulptx_sge_pair *)(sgl + 1);
3358c2ecf20Sopenharmony_ci			} else if ((i - 1) & 0x1) {
3368c2ecf20Sopenharmony_ci				sge_pair->addr[1] = cpu_to_be64(dma_buf->paddr);
3378c2ecf20Sopenharmony_ci				sge_pair->len[1] = cpu_to_be32(
3388c2ecf20Sopenharmony_ci						min(xfer_len, dma_buf->len));
3398c2ecf20Sopenharmony_ci				sge_pair++;
3408c2ecf20Sopenharmony_ci			} else {
3418c2ecf20Sopenharmony_ci				sge_pair->addr[0] = cpu_to_be64(dma_buf->paddr);
3428c2ecf20Sopenharmony_ci				sge_pair->len[0] = cpu_to_be32(
3438c2ecf20Sopenharmony_ci						min(xfer_len, dma_buf->len));
3448c2ecf20Sopenharmony_ci			}
3458c2ecf20Sopenharmony_ci			xfer_len -= min(xfer_len, dma_buf->len);
3468c2ecf20Sopenharmony_ci			i++;
3478c2ecf20Sopenharmony_ci		}
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci/*
3528c2ecf20Sopenharmony_ci * csio_scsi_init_read_wr - Initialize the READ SCSI WR.
3538c2ecf20Sopenharmony_ci * @req: IO req structure.
3548c2ecf20Sopenharmony_ci * @wrp: DMA location to place the payload.
3558c2ecf20Sopenharmony_ci * @size: Size of WR (including FW WR + immed data + rsp SG entry + data SGL
3568c2ecf20Sopenharmony_ci *
3578c2ecf20Sopenharmony_ci * Wrapper for populating fw_scsi_read_wr.
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_cistatic inline void
3608c2ecf20Sopenharmony_cicsio_scsi_init_read_wr(struct csio_ioreq *req, void *wrp, uint32_t size)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
3638c2ecf20Sopenharmony_ci	struct csio_rnode *rn = req->rnode;
3648c2ecf20Sopenharmony_ci	struct fw_scsi_read_wr *wr = (struct fw_scsi_read_wr *)wrp;
3658c2ecf20Sopenharmony_ci	struct ulptx_sgl *sgl;
3668c2ecf20Sopenharmony_ci	struct csio_dma_buf *dma_buf;
3678c2ecf20Sopenharmony_ci	uint8_t imm = csio_hw_to_scsim(hw)->proto_cmd_len;
3688c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	wr->op_immdlen = cpu_to_be32(FW_WR_OP_V(FW_SCSI_READ_WR) |
3718c2ecf20Sopenharmony_ci				     FW_SCSI_READ_WR_IMMDLEN(imm));
3728c2ecf20Sopenharmony_ci	wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID_V(rn->flowid) |
3738c2ecf20Sopenharmony_ci				       FW_WR_LEN16_V(DIV_ROUND_UP(size, 16)));
3748c2ecf20Sopenharmony_ci	wr->cookie = (uintptr_t)req;
3758c2ecf20Sopenharmony_ci	wr->iqid = cpu_to_be16(csio_q_physiqid(hw, req->iq_idx));
3768c2ecf20Sopenharmony_ci	wr->tmo_val = (uint8_t)(req->tmo);
3778c2ecf20Sopenharmony_ci	wr->use_xfer_cnt = 1;
3788c2ecf20Sopenharmony_ci	wr->xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd));
3798c2ecf20Sopenharmony_ci	wr->ini_xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd));
3808c2ecf20Sopenharmony_ci	/* Get RSP DMA buffer */
3818c2ecf20Sopenharmony_ci	dma_buf = &req->dma_buf;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* Prepare RSP SGL */
3848c2ecf20Sopenharmony_ci	wr->rsp_dmalen = cpu_to_be32(dma_buf->len);
3858c2ecf20Sopenharmony_ci	wr->rsp_dmaaddr = cpu_to_be64(dma_buf->paddr);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	wr->r4 = 0;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	wr->u.fcoe.ctl_pri = 0;
3908c2ecf20Sopenharmony_ci	wr->u.fcoe.cp_en_class = 0;
3918c2ecf20Sopenharmony_ci	wr->u.fcoe.r3_lo[0] = 0;
3928c2ecf20Sopenharmony_ci	wr->u.fcoe.r3_lo[1] = 0;
3938c2ecf20Sopenharmony_ci	csio_scsi_fcp_cmnd(req, (void *)((uintptr_t)wrp +
3948c2ecf20Sopenharmony_ci					sizeof(struct fw_scsi_read_wr)));
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* Move WR pointer past command and immediate data */
3978c2ecf20Sopenharmony_ci	sgl = (struct ulptx_sgl *)((uintptr_t)wrp +
3988c2ecf20Sopenharmony_ci			      sizeof(struct fw_scsi_read_wr) + ALIGN(imm, 16));
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* Fill in the DSGL */
4018c2ecf20Sopenharmony_ci	csio_scsi_init_ultptx_dsgl(hw, req, sgl);
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci/*
4058c2ecf20Sopenharmony_ci * csio_scsi_init_write_wr - Initialize the WRITE SCSI WR.
4068c2ecf20Sopenharmony_ci * @req: IO req structure.
4078c2ecf20Sopenharmony_ci * @wrp: DMA location to place the payload.
4088c2ecf20Sopenharmony_ci * @size: Size of WR (including FW WR + immed data + rsp SG entry + data SGL
4098c2ecf20Sopenharmony_ci *
4108c2ecf20Sopenharmony_ci * Wrapper for populating fw_scsi_write_wr.
4118c2ecf20Sopenharmony_ci */
4128c2ecf20Sopenharmony_cistatic inline void
4138c2ecf20Sopenharmony_cicsio_scsi_init_write_wr(struct csio_ioreq *req, void *wrp, uint32_t size)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
4168c2ecf20Sopenharmony_ci	struct csio_rnode *rn = req->rnode;
4178c2ecf20Sopenharmony_ci	struct fw_scsi_write_wr *wr = (struct fw_scsi_write_wr *)wrp;
4188c2ecf20Sopenharmony_ci	struct ulptx_sgl *sgl;
4198c2ecf20Sopenharmony_ci	struct csio_dma_buf *dma_buf;
4208c2ecf20Sopenharmony_ci	uint8_t imm = csio_hw_to_scsim(hw)->proto_cmd_len;
4218c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	wr->op_immdlen = cpu_to_be32(FW_WR_OP_V(FW_SCSI_WRITE_WR) |
4248c2ecf20Sopenharmony_ci				     FW_SCSI_WRITE_WR_IMMDLEN(imm));
4258c2ecf20Sopenharmony_ci	wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID_V(rn->flowid) |
4268c2ecf20Sopenharmony_ci				       FW_WR_LEN16_V(DIV_ROUND_UP(size, 16)));
4278c2ecf20Sopenharmony_ci	wr->cookie = (uintptr_t)req;
4288c2ecf20Sopenharmony_ci	wr->iqid = cpu_to_be16(csio_q_physiqid(hw, req->iq_idx));
4298c2ecf20Sopenharmony_ci	wr->tmo_val = (uint8_t)(req->tmo);
4308c2ecf20Sopenharmony_ci	wr->use_xfer_cnt = 1;
4318c2ecf20Sopenharmony_ci	wr->xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd));
4328c2ecf20Sopenharmony_ci	wr->ini_xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd));
4338c2ecf20Sopenharmony_ci	/* Get RSP DMA buffer */
4348c2ecf20Sopenharmony_ci	dma_buf = &req->dma_buf;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	/* Prepare RSP SGL */
4378c2ecf20Sopenharmony_ci	wr->rsp_dmalen = cpu_to_be32(dma_buf->len);
4388c2ecf20Sopenharmony_ci	wr->rsp_dmaaddr = cpu_to_be64(dma_buf->paddr);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	wr->r4 = 0;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	wr->u.fcoe.ctl_pri = 0;
4438c2ecf20Sopenharmony_ci	wr->u.fcoe.cp_en_class = 0;
4448c2ecf20Sopenharmony_ci	wr->u.fcoe.r3_lo[0] = 0;
4458c2ecf20Sopenharmony_ci	wr->u.fcoe.r3_lo[1] = 0;
4468c2ecf20Sopenharmony_ci	csio_scsi_fcp_cmnd(req, (void *)((uintptr_t)wrp +
4478c2ecf20Sopenharmony_ci					sizeof(struct fw_scsi_write_wr)));
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	/* Move WR pointer past command and immediate data */
4508c2ecf20Sopenharmony_ci	sgl = (struct ulptx_sgl *)((uintptr_t)wrp +
4518c2ecf20Sopenharmony_ci			      sizeof(struct fw_scsi_write_wr) + ALIGN(imm, 16));
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* Fill in the DSGL */
4548c2ecf20Sopenharmony_ci	csio_scsi_init_ultptx_dsgl(hw, req, sgl);
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci/* Calculate WR size needed for fw_scsi_read_wr/fw_scsi_write_wr */
4588c2ecf20Sopenharmony_ci#define CSIO_SCSI_DATA_WRSZ(req, oper, sz, imm)				       \
4598c2ecf20Sopenharmony_cido {									       \
4608c2ecf20Sopenharmony_ci	(sz) = sizeof(struct fw_scsi_##oper##_wr) +	/* WR size */          \
4618c2ecf20Sopenharmony_ci	       ALIGN((imm), 16) +			/* Immed data */       \
4628c2ecf20Sopenharmony_ci	       sizeof(struct ulptx_sgl);		/* ulptx_sgl */	       \
4638c2ecf20Sopenharmony_ci									       \
4648c2ecf20Sopenharmony_ci	if (unlikely((req)->nsge > 1))				               \
4658c2ecf20Sopenharmony_ci		(sz) += (sizeof(struct ulptx_sge_pair) *		       \
4668c2ecf20Sopenharmony_ci				(ALIGN(((req)->nsge - 1), 2) / 2));            \
4678c2ecf20Sopenharmony_ci							/* Data SGE */	       \
4688c2ecf20Sopenharmony_ci} while (0)
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci/*
4718c2ecf20Sopenharmony_ci * csio_scsi_read - Create a SCSI READ WR.
4728c2ecf20Sopenharmony_ci * @req: IO req structure.
4738c2ecf20Sopenharmony_ci *
4748c2ecf20Sopenharmony_ci * Gets a WR slot in the ingress queue and initializes it with
4758c2ecf20Sopenharmony_ci * SCSI READ WR.
4768c2ecf20Sopenharmony_ci *
4778c2ecf20Sopenharmony_ci */
4788c2ecf20Sopenharmony_cistatic inline void
4798c2ecf20Sopenharmony_cicsio_scsi_read(struct csio_ioreq *req)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	struct csio_wr_pair wrp;
4828c2ecf20Sopenharmony_ci	uint32_t size;
4838c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
4848c2ecf20Sopenharmony_ci	struct csio_scsim *scsim = csio_hw_to_scsim(hw);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	CSIO_SCSI_DATA_WRSZ(req, read, size, scsim->proto_cmd_len);
4878c2ecf20Sopenharmony_ci	size = ALIGN(size, 16);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp);
4908c2ecf20Sopenharmony_ci	if (likely(req->drv_status == 0)) {
4918c2ecf20Sopenharmony_ci		if (likely(wrp.size1 >= size)) {
4928c2ecf20Sopenharmony_ci			/* Initialize WR in one shot */
4938c2ecf20Sopenharmony_ci			csio_scsi_init_read_wr(req, wrp.addr1, size);
4948c2ecf20Sopenharmony_ci		} else {
4958c2ecf20Sopenharmony_ci			uint8_t *tmpwr = csio_q_eq_wrap(hw, req->eq_idx);
4968c2ecf20Sopenharmony_ci			/*
4978c2ecf20Sopenharmony_ci			 * Make a temporary copy of the WR and write back
4988c2ecf20Sopenharmony_ci			 * the copy into the WR pair.
4998c2ecf20Sopenharmony_ci			 */
5008c2ecf20Sopenharmony_ci			csio_scsi_init_read_wr(req, (void *)tmpwr, size);
5018c2ecf20Sopenharmony_ci			memcpy(wrp.addr1, tmpwr, wrp.size1);
5028c2ecf20Sopenharmony_ci			memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1);
5038c2ecf20Sopenharmony_ci		}
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci/*
5088c2ecf20Sopenharmony_ci * csio_scsi_write - Create a SCSI WRITE WR.
5098c2ecf20Sopenharmony_ci * @req: IO req structure.
5108c2ecf20Sopenharmony_ci *
5118c2ecf20Sopenharmony_ci * Gets a WR slot in the ingress queue and initializes it with
5128c2ecf20Sopenharmony_ci * SCSI WRITE WR.
5138c2ecf20Sopenharmony_ci *
5148c2ecf20Sopenharmony_ci */
5158c2ecf20Sopenharmony_cistatic inline void
5168c2ecf20Sopenharmony_cicsio_scsi_write(struct csio_ioreq *req)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	struct csio_wr_pair wrp;
5198c2ecf20Sopenharmony_ci	uint32_t size;
5208c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
5218c2ecf20Sopenharmony_ci	struct csio_scsim *scsim = csio_hw_to_scsim(hw);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	CSIO_SCSI_DATA_WRSZ(req, write, size, scsim->proto_cmd_len);
5248c2ecf20Sopenharmony_ci	size = ALIGN(size, 16);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp);
5278c2ecf20Sopenharmony_ci	if (likely(req->drv_status == 0)) {
5288c2ecf20Sopenharmony_ci		if (likely(wrp.size1 >= size)) {
5298c2ecf20Sopenharmony_ci			/* Initialize WR in one shot */
5308c2ecf20Sopenharmony_ci			csio_scsi_init_write_wr(req, wrp.addr1, size);
5318c2ecf20Sopenharmony_ci		} else {
5328c2ecf20Sopenharmony_ci			uint8_t *tmpwr = csio_q_eq_wrap(hw, req->eq_idx);
5338c2ecf20Sopenharmony_ci			/*
5348c2ecf20Sopenharmony_ci			 * Make a temporary copy of the WR and write back
5358c2ecf20Sopenharmony_ci			 * the copy into the WR pair.
5368c2ecf20Sopenharmony_ci			 */
5378c2ecf20Sopenharmony_ci			csio_scsi_init_write_wr(req, (void *)tmpwr, size);
5388c2ecf20Sopenharmony_ci			memcpy(wrp.addr1, tmpwr, wrp.size1);
5398c2ecf20Sopenharmony_ci			memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1);
5408c2ecf20Sopenharmony_ci		}
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci/*
5458c2ecf20Sopenharmony_ci * csio_setup_ddp - Setup DDP buffers for Read request.
5468c2ecf20Sopenharmony_ci * @req: IO req structure.
5478c2ecf20Sopenharmony_ci *
5488c2ecf20Sopenharmony_ci * Checks SGLs/Data buffers are virtually contiguous required for DDP.
5498c2ecf20Sopenharmony_ci * If contiguous,driver posts SGLs in the WR otherwise post internal
5508c2ecf20Sopenharmony_ci * buffers for such request for DDP.
5518c2ecf20Sopenharmony_ci */
5528c2ecf20Sopenharmony_cistatic inline void
5538c2ecf20Sopenharmony_cicsio_setup_ddp(struct csio_scsim *scsim, struct csio_ioreq *req)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci#ifdef __CSIO_DEBUG__
5568c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
5578c2ecf20Sopenharmony_ci#endif
5588c2ecf20Sopenharmony_ci	struct scatterlist *sgel = NULL;
5598c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmnd = csio_scsi_cmnd(req);
5608c2ecf20Sopenharmony_ci	uint64_t sg_addr = 0;
5618c2ecf20Sopenharmony_ci	uint32_t ddp_pagesz = 4096;
5628c2ecf20Sopenharmony_ci	uint32_t buf_off;
5638c2ecf20Sopenharmony_ci	struct csio_dma_buf *dma_buf = NULL;
5648c2ecf20Sopenharmony_ci	uint32_t alloc_len = 0;
5658c2ecf20Sopenharmony_ci	uint32_t xfer_len = 0;
5668c2ecf20Sopenharmony_ci	uint32_t sg_len = 0;
5678c2ecf20Sopenharmony_ci	uint32_t i;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	scsi_for_each_sg(scmnd, sgel, req->nsge, i) {
5708c2ecf20Sopenharmony_ci		sg_addr = sg_dma_address(sgel);
5718c2ecf20Sopenharmony_ci		sg_len	= sg_dma_len(sgel);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci		buf_off = sg_addr & (ddp_pagesz - 1);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		/* Except 1st buffer,all buffer addr have to be Page aligned */
5768c2ecf20Sopenharmony_ci		if (i != 0 && buf_off) {
5778c2ecf20Sopenharmony_ci			csio_dbg(hw, "SGL addr not DDP aligned (%llx:%d)\n",
5788c2ecf20Sopenharmony_ci				 sg_addr, sg_len);
5798c2ecf20Sopenharmony_ci			goto unaligned;
5808c2ecf20Sopenharmony_ci		}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci		/* Except last buffer,all buffer must end on page boundary */
5838c2ecf20Sopenharmony_ci		if ((i != (req->nsge - 1)) &&
5848c2ecf20Sopenharmony_ci			((buf_off + sg_len) & (ddp_pagesz - 1))) {
5858c2ecf20Sopenharmony_ci			csio_dbg(hw,
5868c2ecf20Sopenharmony_ci				 "SGL addr not ending on page boundary"
5878c2ecf20Sopenharmony_ci				 "(%llx:%d)\n", sg_addr, sg_len);
5888c2ecf20Sopenharmony_ci			goto unaligned;
5898c2ecf20Sopenharmony_ci		}
5908c2ecf20Sopenharmony_ci	}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	/* SGL's are virtually contiguous. HW will DDP to SGLs */
5938c2ecf20Sopenharmony_ci	req->dcopy = 0;
5948c2ecf20Sopenharmony_ci	csio_scsi_read(req);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	return;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ciunaligned:
5998c2ecf20Sopenharmony_ci	CSIO_INC_STATS(scsim, n_unaligned);
6008c2ecf20Sopenharmony_ci	/*
6018c2ecf20Sopenharmony_ci	 * For unaligned SGLs, driver will allocate internal DDP buffer.
6028c2ecf20Sopenharmony_ci	 * Once command is completed data from DDP buffer copied to SGLs
6038c2ecf20Sopenharmony_ci	 */
6048c2ecf20Sopenharmony_ci	req->dcopy = 1;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	/* Use gen_list to store the DDP buffers */
6078c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&req->gen_list);
6088c2ecf20Sopenharmony_ci	xfer_len = scsi_bufflen(scmnd);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	i = 0;
6118c2ecf20Sopenharmony_ci	/* Allocate ddp buffers for this request */
6128c2ecf20Sopenharmony_ci	while (alloc_len < xfer_len) {
6138c2ecf20Sopenharmony_ci		dma_buf = csio_get_scsi_ddp(scsim);
6148c2ecf20Sopenharmony_ci		if (dma_buf == NULL || i > scsim->max_sge) {
6158c2ecf20Sopenharmony_ci			req->drv_status = -EBUSY;
6168c2ecf20Sopenharmony_ci			break;
6178c2ecf20Sopenharmony_ci		}
6188c2ecf20Sopenharmony_ci		alloc_len += dma_buf->len;
6198c2ecf20Sopenharmony_ci		/* Added to IO req */
6208c2ecf20Sopenharmony_ci		list_add_tail(&dma_buf->list, &req->gen_list);
6218c2ecf20Sopenharmony_ci		i++;
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (!req->drv_status) {
6258c2ecf20Sopenharmony_ci		/* set number of ddp bufs used */
6268c2ecf20Sopenharmony_ci		req->nsge = i;
6278c2ecf20Sopenharmony_ci		csio_scsi_read(req);
6288c2ecf20Sopenharmony_ci		return;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	 /* release dma descs */
6328c2ecf20Sopenharmony_ci	if (i > 0)
6338c2ecf20Sopenharmony_ci		csio_put_scsi_ddp_list(scsim, &req->gen_list, i);
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci/*
6378c2ecf20Sopenharmony_ci * csio_scsi_init_abrt_cls_wr - Initialize an ABORT/CLOSE WR.
6388c2ecf20Sopenharmony_ci * @req: IO req structure.
6398c2ecf20Sopenharmony_ci * @addr: DMA location to place the payload.
6408c2ecf20Sopenharmony_ci * @size: Size of WR
6418c2ecf20Sopenharmony_ci * @abort: abort OR close
6428c2ecf20Sopenharmony_ci *
6438c2ecf20Sopenharmony_ci * Wrapper for populating fw_scsi_cmd_wr.
6448c2ecf20Sopenharmony_ci */
6458c2ecf20Sopenharmony_cistatic inline void
6468c2ecf20Sopenharmony_cicsio_scsi_init_abrt_cls_wr(struct csio_ioreq *req, void *addr, uint32_t size,
6478c2ecf20Sopenharmony_ci			   bool abort)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
6508c2ecf20Sopenharmony_ci	struct csio_rnode *rn = req->rnode;
6518c2ecf20Sopenharmony_ci	struct fw_scsi_abrt_cls_wr *wr = (struct fw_scsi_abrt_cls_wr *)addr;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	wr->op_immdlen = cpu_to_be32(FW_WR_OP_V(FW_SCSI_ABRT_CLS_WR));
6548c2ecf20Sopenharmony_ci	wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID_V(rn->flowid) |
6558c2ecf20Sopenharmony_ci					    FW_WR_LEN16_V(
6568c2ecf20Sopenharmony_ci						DIV_ROUND_UP(size, 16)));
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	wr->cookie = (uintptr_t) req;
6598c2ecf20Sopenharmony_ci	wr->iqid = cpu_to_be16(csio_q_physiqid(hw, req->iq_idx));
6608c2ecf20Sopenharmony_ci	wr->tmo_val = (uint8_t) req->tmo;
6618c2ecf20Sopenharmony_ci	/* 0 for CHK_ALL_IO tells FW to look up t_cookie */
6628c2ecf20Sopenharmony_ci	wr->sub_opcode_to_chk_all_io =
6638c2ecf20Sopenharmony_ci				(FW_SCSI_ABRT_CLS_WR_SUB_OPCODE(abort) |
6648c2ecf20Sopenharmony_ci				 FW_SCSI_ABRT_CLS_WR_CHK_ALL_IO(0));
6658c2ecf20Sopenharmony_ci	wr->r3[0] = 0;
6668c2ecf20Sopenharmony_ci	wr->r3[1] = 0;
6678c2ecf20Sopenharmony_ci	wr->r3[2] = 0;
6688c2ecf20Sopenharmony_ci	wr->r3[3] = 0;
6698c2ecf20Sopenharmony_ci	/* Since we re-use the same ioreq for abort as well */
6708c2ecf20Sopenharmony_ci	wr->t_cookie = (uintptr_t) req;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_cistatic inline void
6748c2ecf20Sopenharmony_cicsio_scsi_abrt_cls(struct csio_ioreq *req, bool abort)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	struct csio_wr_pair wrp;
6778c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
6788c2ecf20Sopenharmony_ci	uint32_t size = ALIGN(sizeof(struct fw_scsi_abrt_cls_wr), 16);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp);
6818c2ecf20Sopenharmony_ci	if (req->drv_status != 0)
6828c2ecf20Sopenharmony_ci		return;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (wrp.size1 >= size) {
6858c2ecf20Sopenharmony_ci		/* Initialize WR in one shot */
6868c2ecf20Sopenharmony_ci		csio_scsi_init_abrt_cls_wr(req, wrp.addr1, size, abort);
6878c2ecf20Sopenharmony_ci	} else {
6888c2ecf20Sopenharmony_ci		uint8_t *tmpwr = csio_q_eq_wrap(hw, req->eq_idx);
6898c2ecf20Sopenharmony_ci		/*
6908c2ecf20Sopenharmony_ci		 * Make a temporary copy of the WR and write back
6918c2ecf20Sopenharmony_ci		 * the copy into the WR pair.
6928c2ecf20Sopenharmony_ci		 */
6938c2ecf20Sopenharmony_ci		csio_scsi_init_abrt_cls_wr(req, (void *)tmpwr, size, abort);
6948c2ecf20Sopenharmony_ci		memcpy(wrp.addr1, tmpwr, wrp.size1);
6958c2ecf20Sopenharmony_ci		memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1);
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci/*****************************************************************************/
7008c2ecf20Sopenharmony_ci/* START: SCSI SM                                                            */
7018c2ecf20Sopenharmony_ci/*****************************************************************************/
7028c2ecf20Sopenharmony_cistatic void
7038c2ecf20Sopenharmony_cicsio_scsis_uninit(struct csio_ioreq *req, enum csio_scsi_ev evt)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
7068c2ecf20Sopenharmony_ci	struct csio_scsim *scsim = csio_hw_to_scsim(hw);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	switch (evt) {
7098c2ecf20Sopenharmony_ci	case CSIO_SCSIE_START_IO:
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci		if (req->nsge) {
7128c2ecf20Sopenharmony_ci			if (req->datadir == DMA_TO_DEVICE) {
7138c2ecf20Sopenharmony_ci				req->dcopy = 0;
7148c2ecf20Sopenharmony_ci				csio_scsi_write(req);
7158c2ecf20Sopenharmony_ci			} else
7168c2ecf20Sopenharmony_ci				csio_setup_ddp(scsim, req);
7178c2ecf20Sopenharmony_ci		} else {
7188c2ecf20Sopenharmony_ci			csio_scsi_cmd(req);
7198c2ecf20Sopenharmony_ci		}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci		if (likely(req->drv_status == 0)) {
7228c2ecf20Sopenharmony_ci			/* change state and enqueue on active_q */
7238c2ecf20Sopenharmony_ci			csio_set_state(&req->sm, csio_scsis_io_active);
7248c2ecf20Sopenharmony_ci			list_add_tail(&req->sm.sm_list, &scsim->active_q);
7258c2ecf20Sopenharmony_ci			csio_wr_issue(hw, req->eq_idx, false);
7268c2ecf20Sopenharmony_ci			CSIO_INC_STATS(scsim, n_active);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci			return;
7298c2ecf20Sopenharmony_ci		}
7308c2ecf20Sopenharmony_ci		break;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	case CSIO_SCSIE_START_TM:
7338c2ecf20Sopenharmony_ci		csio_scsi_cmd(req);
7348c2ecf20Sopenharmony_ci		if (req->drv_status == 0) {
7358c2ecf20Sopenharmony_ci			/*
7368c2ecf20Sopenharmony_ci			 * NOTE: We collect the affected I/Os prior to issuing
7378c2ecf20Sopenharmony_ci			 * LUN reset, and not after it. This is to prevent
7388c2ecf20Sopenharmony_ci			 * aborting I/Os that get issued after the LUN reset,
7398c2ecf20Sopenharmony_ci			 * but prior to LUN reset completion (in the event that
7408c2ecf20Sopenharmony_ci			 * the host stack has not blocked I/Os to a LUN that is
7418c2ecf20Sopenharmony_ci			 * being reset.
7428c2ecf20Sopenharmony_ci			 */
7438c2ecf20Sopenharmony_ci			csio_set_state(&req->sm, csio_scsis_tm_active);
7448c2ecf20Sopenharmony_ci			list_add_tail(&req->sm.sm_list, &scsim->active_q);
7458c2ecf20Sopenharmony_ci			csio_wr_issue(hw, req->eq_idx, false);
7468c2ecf20Sopenharmony_ci			CSIO_INC_STATS(scsim, n_tm_active);
7478c2ecf20Sopenharmony_ci		}
7488c2ecf20Sopenharmony_ci		return;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	case CSIO_SCSIE_ABORT:
7518c2ecf20Sopenharmony_ci	case CSIO_SCSIE_CLOSE:
7528c2ecf20Sopenharmony_ci		/*
7538c2ecf20Sopenharmony_ci		 * NOTE:
7548c2ecf20Sopenharmony_ci		 * We could get here due to  :
7558c2ecf20Sopenharmony_ci		 * - a window in the cleanup path of the SCSI module
7568c2ecf20Sopenharmony_ci		 *   (csio_scsi_abort_io()). Please see NOTE in this function.
7578c2ecf20Sopenharmony_ci		 * - a window in the time we tried to issue an abort/close
7588c2ecf20Sopenharmony_ci		 *   of a request to FW, and the FW completed the request
7598c2ecf20Sopenharmony_ci		 *   itself.
7608c2ecf20Sopenharmony_ci		 *   Print a message for now, and return INVAL either way.
7618c2ecf20Sopenharmony_ci		 */
7628c2ecf20Sopenharmony_ci		req->drv_status = -EINVAL;
7638c2ecf20Sopenharmony_ci		csio_warn(hw, "Trying to abort/close completed IO:%p!\n", req);
7648c2ecf20Sopenharmony_ci		break;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	default:
7678c2ecf20Sopenharmony_ci		csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
7688c2ecf20Sopenharmony_ci		CSIO_DB_ASSERT(0);
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_cistatic void
7738c2ecf20Sopenharmony_cicsio_scsis_io_active(struct csio_ioreq *req, enum csio_scsi_ev evt)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
7768c2ecf20Sopenharmony_ci	struct csio_scsim *scm = csio_hw_to_scsim(hw);
7778c2ecf20Sopenharmony_ci	struct csio_rnode *rn;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	switch (evt) {
7808c2ecf20Sopenharmony_ci	case CSIO_SCSIE_COMPLETED:
7818c2ecf20Sopenharmony_ci		CSIO_DEC_STATS(scm, n_active);
7828c2ecf20Sopenharmony_ci		list_del_init(&req->sm.sm_list);
7838c2ecf20Sopenharmony_ci		csio_set_state(&req->sm, csio_scsis_uninit);
7848c2ecf20Sopenharmony_ci		/*
7858c2ecf20Sopenharmony_ci		 * In MSIX mode, with multiple queues, the SCSI compeltions
7868c2ecf20Sopenharmony_ci		 * could reach us sooner than the FW events sent to indicate
7878c2ecf20Sopenharmony_ci		 * I-T nexus loss (link down, remote device logo etc). We
7888c2ecf20Sopenharmony_ci		 * dont want to be returning such I/Os to the upper layer
7898c2ecf20Sopenharmony_ci		 * immediately, since we wouldnt have reported the I-T nexus
7908c2ecf20Sopenharmony_ci		 * loss itself. This forces us to serialize such completions
7918c2ecf20Sopenharmony_ci		 * with the reporting of the I-T nexus loss. Therefore, we
7928c2ecf20Sopenharmony_ci		 * internally queue up such up such completions in the rnode.
7938c2ecf20Sopenharmony_ci		 * The reporting of I-T nexus loss to the upper layer is then
7948c2ecf20Sopenharmony_ci		 * followed by the returning of I/Os in this internal queue.
7958c2ecf20Sopenharmony_ci		 * Having another state alongwith another queue helps us take
7968c2ecf20Sopenharmony_ci		 * actions for events such as ABORT received while we are
7978c2ecf20Sopenharmony_ci		 * in this rnode queue.
7988c2ecf20Sopenharmony_ci		 */
7998c2ecf20Sopenharmony_ci		if (unlikely(req->wr_status != FW_SUCCESS)) {
8008c2ecf20Sopenharmony_ci			rn = req->rnode;
8018c2ecf20Sopenharmony_ci			/*
8028c2ecf20Sopenharmony_ci			 * FW says remote device is lost, but rnode
8038c2ecf20Sopenharmony_ci			 * doesnt reflect it.
8048c2ecf20Sopenharmony_ci			 */
8058c2ecf20Sopenharmony_ci			if (csio_scsi_itnexus_loss_error(req->wr_status) &&
8068c2ecf20Sopenharmony_ci						csio_is_rnode_ready(rn)) {
8078c2ecf20Sopenharmony_ci				csio_set_state(&req->sm,
8088c2ecf20Sopenharmony_ci						csio_scsis_shost_cmpl_await);
8098c2ecf20Sopenharmony_ci				list_add_tail(&req->sm.sm_list,
8108c2ecf20Sopenharmony_ci					      &rn->host_cmpl_q);
8118c2ecf20Sopenharmony_ci			}
8128c2ecf20Sopenharmony_ci		}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci		break;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	case CSIO_SCSIE_ABORT:
8178c2ecf20Sopenharmony_ci		csio_scsi_abrt_cls(req, SCSI_ABORT);
8188c2ecf20Sopenharmony_ci		if (req->drv_status == 0) {
8198c2ecf20Sopenharmony_ci			csio_wr_issue(hw, req->eq_idx, false);
8208c2ecf20Sopenharmony_ci			csio_set_state(&req->sm, csio_scsis_aborting);
8218c2ecf20Sopenharmony_ci		}
8228c2ecf20Sopenharmony_ci		break;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	case CSIO_SCSIE_CLOSE:
8258c2ecf20Sopenharmony_ci		csio_scsi_abrt_cls(req, SCSI_CLOSE);
8268c2ecf20Sopenharmony_ci		if (req->drv_status == 0) {
8278c2ecf20Sopenharmony_ci			csio_wr_issue(hw, req->eq_idx, false);
8288c2ecf20Sopenharmony_ci			csio_set_state(&req->sm, csio_scsis_closing);
8298c2ecf20Sopenharmony_ci		}
8308c2ecf20Sopenharmony_ci		break;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	case CSIO_SCSIE_DRVCLEANUP:
8338c2ecf20Sopenharmony_ci		req->wr_status = FW_HOSTERROR;
8348c2ecf20Sopenharmony_ci		CSIO_DEC_STATS(scm, n_active);
8358c2ecf20Sopenharmony_ci		csio_set_state(&req->sm, csio_scsis_uninit);
8368c2ecf20Sopenharmony_ci		break;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	default:
8398c2ecf20Sopenharmony_ci		csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
8408c2ecf20Sopenharmony_ci		CSIO_DB_ASSERT(0);
8418c2ecf20Sopenharmony_ci	}
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_cistatic void
8458c2ecf20Sopenharmony_cicsio_scsis_tm_active(struct csio_ioreq *req, enum csio_scsi_ev evt)
8468c2ecf20Sopenharmony_ci{
8478c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
8488c2ecf20Sopenharmony_ci	struct csio_scsim *scm = csio_hw_to_scsim(hw);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	switch (evt) {
8518c2ecf20Sopenharmony_ci	case CSIO_SCSIE_COMPLETED:
8528c2ecf20Sopenharmony_ci		CSIO_DEC_STATS(scm, n_tm_active);
8538c2ecf20Sopenharmony_ci		list_del_init(&req->sm.sm_list);
8548c2ecf20Sopenharmony_ci		csio_set_state(&req->sm, csio_scsis_uninit);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci		break;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	case CSIO_SCSIE_ABORT:
8598c2ecf20Sopenharmony_ci		csio_scsi_abrt_cls(req, SCSI_ABORT);
8608c2ecf20Sopenharmony_ci		if (req->drv_status == 0) {
8618c2ecf20Sopenharmony_ci			csio_wr_issue(hw, req->eq_idx, false);
8628c2ecf20Sopenharmony_ci			csio_set_state(&req->sm, csio_scsis_aborting);
8638c2ecf20Sopenharmony_ci		}
8648c2ecf20Sopenharmony_ci		break;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	case CSIO_SCSIE_CLOSE:
8688c2ecf20Sopenharmony_ci		csio_scsi_abrt_cls(req, SCSI_CLOSE);
8698c2ecf20Sopenharmony_ci		if (req->drv_status == 0) {
8708c2ecf20Sopenharmony_ci			csio_wr_issue(hw, req->eq_idx, false);
8718c2ecf20Sopenharmony_ci			csio_set_state(&req->sm, csio_scsis_closing);
8728c2ecf20Sopenharmony_ci		}
8738c2ecf20Sopenharmony_ci		break;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	case CSIO_SCSIE_DRVCLEANUP:
8768c2ecf20Sopenharmony_ci		req->wr_status = FW_HOSTERROR;
8778c2ecf20Sopenharmony_ci		CSIO_DEC_STATS(scm, n_tm_active);
8788c2ecf20Sopenharmony_ci		csio_set_state(&req->sm, csio_scsis_uninit);
8798c2ecf20Sopenharmony_ci		break;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	default:
8828c2ecf20Sopenharmony_ci		csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
8838c2ecf20Sopenharmony_ci		CSIO_DB_ASSERT(0);
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci}
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_cistatic void
8888c2ecf20Sopenharmony_cicsio_scsis_aborting(struct csio_ioreq *req, enum csio_scsi_ev evt)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
8918c2ecf20Sopenharmony_ci	struct csio_scsim *scm = csio_hw_to_scsim(hw);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	switch (evt) {
8948c2ecf20Sopenharmony_ci	case CSIO_SCSIE_COMPLETED:
8958c2ecf20Sopenharmony_ci		csio_dbg(hw,
8968c2ecf20Sopenharmony_ci			 "ioreq %p recvd cmpltd (wr_status:%d) "
8978c2ecf20Sopenharmony_ci			 "in aborting st\n", req, req->wr_status);
8988c2ecf20Sopenharmony_ci		/*
8998c2ecf20Sopenharmony_ci		 * Use -ECANCELED to explicitly tell the ABORTED event that
9008c2ecf20Sopenharmony_ci		 * the original I/O was returned to driver by FW.
9018c2ecf20Sopenharmony_ci		 * We dont really care if the I/O was returned with success by
9028c2ecf20Sopenharmony_ci		 * FW (because the ABORT and completion of the I/O crossed each
9038c2ecf20Sopenharmony_ci		 * other), or any other return value. Once we are in aborting
9048c2ecf20Sopenharmony_ci		 * state, the success or failure of the I/O is unimportant to
9058c2ecf20Sopenharmony_ci		 * us.
9068c2ecf20Sopenharmony_ci		 */
9078c2ecf20Sopenharmony_ci		req->drv_status = -ECANCELED;
9088c2ecf20Sopenharmony_ci		break;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	case CSIO_SCSIE_ABORT:
9118c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_abrt_dups);
9128c2ecf20Sopenharmony_ci		break;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	case CSIO_SCSIE_ABORTED:
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci		csio_dbg(hw, "abort of %p return status:0x%x drv_status:%x\n",
9178c2ecf20Sopenharmony_ci			 req, req->wr_status, req->drv_status);
9188c2ecf20Sopenharmony_ci		/*
9198c2ecf20Sopenharmony_ci		 * Check if original I/O WR completed before the Abort
9208c2ecf20Sopenharmony_ci		 * completion.
9218c2ecf20Sopenharmony_ci		 */
9228c2ecf20Sopenharmony_ci		if (req->drv_status != -ECANCELED) {
9238c2ecf20Sopenharmony_ci			csio_warn(hw,
9248c2ecf20Sopenharmony_ci				  "Abort completed before original I/O,"
9258c2ecf20Sopenharmony_ci				   " req:%p\n", req);
9268c2ecf20Sopenharmony_ci			CSIO_DB_ASSERT(0);
9278c2ecf20Sopenharmony_ci		}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci		/*
9308c2ecf20Sopenharmony_ci		 * There are the following possible scenarios:
9318c2ecf20Sopenharmony_ci		 * 1. The abort completed successfully, FW returned FW_SUCCESS.
9328c2ecf20Sopenharmony_ci		 * 2. The completion of an I/O and the receipt of
9338c2ecf20Sopenharmony_ci		 *    abort for that I/O by the FW crossed each other.
9348c2ecf20Sopenharmony_ci		 *    The FW returned FW_EINVAL. The original I/O would have
9358c2ecf20Sopenharmony_ci		 *    returned with FW_SUCCESS or any other SCSI error.
9368c2ecf20Sopenharmony_ci		 * 3. The FW couldn't sent the abort out on the wire, as there
9378c2ecf20Sopenharmony_ci		 *    was an I-T nexus loss (link down, remote device logged
9388c2ecf20Sopenharmony_ci		 *    out etc). FW sent back an appropriate IT nexus loss status
9398c2ecf20Sopenharmony_ci		 *    for the abort.
9408c2ecf20Sopenharmony_ci		 * 4. FW sent an abort, but abort timed out (remote device
9418c2ecf20Sopenharmony_ci		 *    didnt respond). FW replied back with
9428c2ecf20Sopenharmony_ci		 *    FW_SCSI_ABORT_TIMEDOUT.
9438c2ecf20Sopenharmony_ci		 * 5. FW couldn't genuinely abort the request for some reason,
9448c2ecf20Sopenharmony_ci		 *    and sent us an error.
9458c2ecf20Sopenharmony_ci		 *
9468c2ecf20Sopenharmony_ci		 * The first 3 scenarios are treated as  succesful abort
9478c2ecf20Sopenharmony_ci		 * operations by the host, while the last 2 are failed attempts
9488c2ecf20Sopenharmony_ci		 * to abort. Manipulate the return value of the request
9498c2ecf20Sopenharmony_ci		 * appropriately, so that host can convey these results
9508c2ecf20Sopenharmony_ci		 * back to the upper layer.
9518c2ecf20Sopenharmony_ci		 */
9528c2ecf20Sopenharmony_ci		if ((req->wr_status == FW_SUCCESS) ||
9538c2ecf20Sopenharmony_ci		    (req->wr_status == FW_EINVAL) ||
9548c2ecf20Sopenharmony_ci		    csio_scsi_itnexus_loss_error(req->wr_status))
9558c2ecf20Sopenharmony_ci			req->wr_status = FW_SCSI_ABORT_REQUESTED;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci		CSIO_DEC_STATS(scm, n_active);
9588c2ecf20Sopenharmony_ci		list_del_init(&req->sm.sm_list);
9598c2ecf20Sopenharmony_ci		csio_set_state(&req->sm, csio_scsis_uninit);
9608c2ecf20Sopenharmony_ci		break;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	case CSIO_SCSIE_DRVCLEANUP:
9638c2ecf20Sopenharmony_ci		req->wr_status = FW_HOSTERROR;
9648c2ecf20Sopenharmony_ci		CSIO_DEC_STATS(scm, n_active);
9658c2ecf20Sopenharmony_ci		csio_set_state(&req->sm, csio_scsis_uninit);
9668c2ecf20Sopenharmony_ci		break;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	case CSIO_SCSIE_CLOSE:
9698c2ecf20Sopenharmony_ci		/*
9708c2ecf20Sopenharmony_ci		 * We can receive this event from the module
9718c2ecf20Sopenharmony_ci		 * cleanup paths, if the FW forgot to reply to the ABORT WR
9728c2ecf20Sopenharmony_ci		 * and left this ioreq in this state. For now, just ignore
9738c2ecf20Sopenharmony_ci		 * the event. The CLOSE event is sent to this state, as
9748c2ecf20Sopenharmony_ci		 * the LINK may have already gone down.
9758c2ecf20Sopenharmony_ci		 */
9768c2ecf20Sopenharmony_ci		break;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	default:
9798c2ecf20Sopenharmony_ci		csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
9808c2ecf20Sopenharmony_ci		CSIO_DB_ASSERT(0);
9818c2ecf20Sopenharmony_ci	}
9828c2ecf20Sopenharmony_ci}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_cistatic void
9858c2ecf20Sopenharmony_cicsio_scsis_closing(struct csio_ioreq *req, enum csio_scsi_ev evt)
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	struct csio_hw *hw = req->lnode->hwp;
9888c2ecf20Sopenharmony_ci	struct csio_scsim *scm = csio_hw_to_scsim(hw);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	switch (evt) {
9918c2ecf20Sopenharmony_ci	case CSIO_SCSIE_COMPLETED:
9928c2ecf20Sopenharmony_ci		csio_dbg(hw,
9938c2ecf20Sopenharmony_ci			 "ioreq %p recvd cmpltd (wr_status:%d) "
9948c2ecf20Sopenharmony_ci			 "in closing st\n", req, req->wr_status);
9958c2ecf20Sopenharmony_ci		/*
9968c2ecf20Sopenharmony_ci		 * Use -ECANCELED to explicitly tell the CLOSED event that
9978c2ecf20Sopenharmony_ci		 * the original I/O was returned to driver by FW.
9988c2ecf20Sopenharmony_ci		 * We dont really care if the I/O was returned with success by
9998c2ecf20Sopenharmony_ci		 * FW (because the CLOSE and completion of the I/O crossed each
10008c2ecf20Sopenharmony_ci		 * other), or any other return value. Once we are in aborting
10018c2ecf20Sopenharmony_ci		 * state, the success or failure of the I/O is unimportant to
10028c2ecf20Sopenharmony_ci		 * us.
10038c2ecf20Sopenharmony_ci		 */
10048c2ecf20Sopenharmony_ci		req->drv_status = -ECANCELED;
10058c2ecf20Sopenharmony_ci		break;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	case CSIO_SCSIE_CLOSED:
10088c2ecf20Sopenharmony_ci		/*
10098c2ecf20Sopenharmony_ci		 * Check if original I/O WR completed before the Close
10108c2ecf20Sopenharmony_ci		 * completion.
10118c2ecf20Sopenharmony_ci		 */
10128c2ecf20Sopenharmony_ci		if (req->drv_status != -ECANCELED) {
10138c2ecf20Sopenharmony_ci			csio_fatal(hw,
10148c2ecf20Sopenharmony_ci				   "Close completed before original I/O,"
10158c2ecf20Sopenharmony_ci				   " req:%p\n", req);
10168c2ecf20Sopenharmony_ci			CSIO_DB_ASSERT(0);
10178c2ecf20Sopenharmony_ci		}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci		/*
10208c2ecf20Sopenharmony_ci		 * Either close succeeded, or we issued close to FW at the
10218c2ecf20Sopenharmony_ci		 * same time FW compelted it to us. Either way, the I/O
10228c2ecf20Sopenharmony_ci		 * is closed.
10238c2ecf20Sopenharmony_ci		 */
10248c2ecf20Sopenharmony_ci		CSIO_DB_ASSERT((req->wr_status == FW_SUCCESS) ||
10258c2ecf20Sopenharmony_ci					(req->wr_status == FW_EINVAL));
10268c2ecf20Sopenharmony_ci		req->wr_status = FW_SCSI_CLOSE_REQUESTED;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci		CSIO_DEC_STATS(scm, n_active);
10298c2ecf20Sopenharmony_ci		list_del_init(&req->sm.sm_list);
10308c2ecf20Sopenharmony_ci		csio_set_state(&req->sm, csio_scsis_uninit);
10318c2ecf20Sopenharmony_ci		break;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	case CSIO_SCSIE_CLOSE:
10348c2ecf20Sopenharmony_ci		break;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	case CSIO_SCSIE_DRVCLEANUP:
10378c2ecf20Sopenharmony_ci		req->wr_status = FW_HOSTERROR;
10388c2ecf20Sopenharmony_ci		CSIO_DEC_STATS(scm, n_active);
10398c2ecf20Sopenharmony_ci		csio_set_state(&req->sm, csio_scsis_uninit);
10408c2ecf20Sopenharmony_ci		break;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	default:
10438c2ecf20Sopenharmony_ci		csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req);
10448c2ecf20Sopenharmony_ci		CSIO_DB_ASSERT(0);
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_cistatic void
10498c2ecf20Sopenharmony_cicsio_scsis_shost_cmpl_await(struct csio_ioreq *req, enum csio_scsi_ev evt)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	switch (evt) {
10528c2ecf20Sopenharmony_ci	case CSIO_SCSIE_ABORT:
10538c2ecf20Sopenharmony_ci	case CSIO_SCSIE_CLOSE:
10548c2ecf20Sopenharmony_ci		/*
10558c2ecf20Sopenharmony_ci		 * Just succeed the abort request, and hope that
10568c2ecf20Sopenharmony_ci		 * the remote device unregister path will cleanup
10578c2ecf20Sopenharmony_ci		 * this I/O to the upper layer within a sane
10588c2ecf20Sopenharmony_ci		 * amount of time.
10598c2ecf20Sopenharmony_ci		 */
10608c2ecf20Sopenharmony_ci		/*
10618c2ecf20Sopenharmony_ci		 * A close can come in during a LINK DOWN. The FW would have
10628c2ecf20Sopenharmony_ci		 * returned us the I/O back, but not the remote device lost
10638c2ecf20Sopenharmony_ci		 * FW event. In this interval, if the I/O times out at the upper
10648c2ecf20Sopenharmony_ci		 * layer, a close can come in. Take the same action as abort:
10658c2ecf20Sopenharmony_ci		 * return success, and hope that the remote device unregister
10668c2ecf20Sopenharmony_ci		 * path will cleanup this I/O. If the FW still doesnt send
10678c2ecf20Sopenharmony_ci		 * the msg, the close times out, and the upper layer resorts
10688c2ecf20Sopenharmony_ci		 * to the next level of error recovery.
10698c2ecf20Sopenharmony_ci		 */
10708c2ecf20Sopenharmony_ci		req->drv_status = 0;
10718c2ecf20Sopenharmony_ci		break;
10728c2ecf20Sopenharmony_ci	case CSIO_SCSIE_DRVCLEANUP:
10738c2ecf20Sopenharmony_ci		csio_set_state(&req->sm, csio_scsis_uninit);
10748c2ecf20Sopenharmony_ci		break;
10758c2ecf20Sopenharmony_ci	default:
10768c2ecf20Sopenharmony_ci		csio_dbg(req->lnode->hwp, "Unhandled event:%d sent to req:%p\n",
10778c2ecf20Sopenharmony_ci			 evt, req);
10788c2ecf20Sopenharmony_ci		CSIO_DB_ASSERT(0);
10798c2ecf20Sopenharmony_ci	}
10808c2ecf20Sopenharmony_ci}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci/*
10838c2ecf20Sopenharmony_ci * csio_scsi_cmpl_handler - WR completion handler for SCSI.
10848c2ecf20Sopenharmony_ci * @hw: HW module.
10858c2ecf20Sopenharmony_ci * @wr: The completed WR from the ingress queue.
10868c2ecf20Sopenharmony_ci * @len: Length of the WR.
10878c2ecf20Sopenharmony_ci * @flb: Freelist buffer array.
10888c2ecf20Sopenharmony_ci * @priv: Private object
10898c2ecf20Sopenharmony_ci * @scsiwr: Pointer to SCSI WR.
10908c2ecf20Sopenharmony_ci *
10918c2ecf20Sopenharmony_ci * This is the WR completion handler called per completion from the
10928c2ecf20Sopenharmony_ci * ISR. It is called with lock held. It walks past the RSS and CPL message
10938c2ecf20Sopenharmony_ci * header where the actual WR is present.
10948c2ecf20Sopenharmony_ci * It then gets the status, WR handle (ioreq pointer) and the len of
10958c2ecf20Sopenharmony_ci * the WR, based on WR opcode. Only on a non-good status is the entire
10968c2ecf20Sopenharmony_ci * WR copied into the WR cache (ioreq->fw_wr).
10978c2ecf20Sopenharmony_ci * The ioreq corresponding to the WR is returned to the caller.
10988c2ecf20Sopenharmony_ci * NOTE: The SCSI queue doesnt allocate a freelist today, hence
10998c2ecf20Sopenharmony_ci * no freelist buffer is expected.
11008c2ecf20Sopenharmony_ci */
11018c2ecf20Sopenharmony_cistruct csio_ioreq *
11028c2ecf20Sopenharmony_cicsio_scsi_cmpl_handler(struct csio_hw *hw, void *wr, uint32_t len,
11038c2ecf20Sopenharmony_ci		     struct csio_fl_dma_buf *flb, void *priv, uint8_t **scsiwr)
11048c2ecf20Sopenharmony_ci{
11058c2ecf20Sopenharmony_ci	struct csio_ioreq *ioreq = NULL;
11068c2ecf20Sopenharmony_ci	struct cpl_fw6_msg *cpl;
11078c2ecf20Sopenharmony_ci	uint8_t *tempwr;
11088c2ecf20Sopenharmony_ci	uint8_t	status;
11098c2ecf20Sopenharmony_ci	struct csio_scsim *scm = csio_hw_to_scsim(hw);
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	/* skip RSS header */
11128c2ecf20Sopenharmony_ci	cpl = (struct cpl_fw6_msg *)((uintptr_t)wr + sizeof(__be64));
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	if (unlikely(cpl->opcode != CPL_FW6_MSG)) {
11158c2ecf20Sopenharmony_ci		csio_warn(hw, "Error: Invalid CPL msg %x recvd on SCSI q\n",
11168c2ecf20Sopenharmony_ci			  cpl->opcode);
11178c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_inval_cplop);
11188c2ecf20Sopenharmony_ci		return NULL;
11198c2ecf20Sopenharmony_ci	}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	tempwr = (uint8_t *)(cpl->data);
11228c2ecf20Sopenharmony_ci	status = csio_wr_status(tempwr);
11238c2ecf20Sopenharmony_ci	*scsiwr = tempwr;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	if (likely((*tempwr == FW_SCSI_READ_WR) ||
11268c2ecf20Sopenharmony_ci			(*tempwr == FW_SCSI_WRITE_WR) ||
11278c2ecf20Sopenharmony_ci			(*tempwr == FW_SCSI_CMD_WR))) {
11288c2ecf20Sopenharmony_ci		ioreq = (struct csio_ioreq *)((uintptr_t)
11298c2ecf20Sopenharmony_ci				 (((struct fw_scsi_read_wr *)tempwr)->cookie));
11308c2ecf20Sopenharmony_ci		CSIO_DB_ASSERT(virt_addr_valid(ioreq));
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci		ioreq->wr_status = status;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci		return ioreq;
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	if (*tempwr == FW_SCSI_ABRT_CLS_WR) {
11388c2ecf20Sopenharmony_ci		ioreq = (struct csio_ioreq *)((uintptr_t)
11398c2ecf20Sopenharmony_ci			 (((struct fw_scsi_abrt_cls_wr *)tempwr)->cookie));
11408c2ecf20Sopenharmony_ci		CSIO_DB_ASSERT(virt_addr_valid(ioreq));
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci		ioreq->wr_status = status;
11438c2ecf20Sopenharmony_ci		return ioreq;
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	csio_warn(hw, "WR with invalid opcode in SCSI IQ: %x\n", *tempwr);
11478c2ecf20Sopenharmony_ci	CSIO_INC_STATS(scm, n_inval_scsiop);
11488c2ecf20Sopenharmony_ci	return NULL;
11498c2ecf20Sopenharmony_ci}
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci/*
11528c2ecf20Sopenharmony_ci * csio_scsi_cleanup_io_q - Cleanup the given queue.
11538c2ecf20Sopenharmony_ci * @scm: SCSI module.
11548c2ecf20Sopenharmony_ci * @q: Queue to be cleaned up.
11558c2ecf20Sopenharmony_ci *
11568c2ecf20Sopenharmony_ci * Called with lock held. Has to exit with lock held.
11578c2ecf20Sopenharmony_ci */
11588c2ecf20Sopenharmony_civoid
11598c2ecf20Sopenharmony_cicsio_scsi_cleanup_io_q(struct csio_scsim *scm, struct list_head *q)
11608c2ecf20Sopenharmony_ci{
11618c2ecf20Sopenharmony_ci	struct csio_hw *hw = scm->hw;
11628c2ecf20Sopenharmony_ci	struct csio_ioreq *ioreq;
11638c2ecf20Sopenharmony_ci	struct list_head *tmp, *next;
11648c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmnd;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	/* Call back the completion routines of the active_q */
11678c2ecf20Sopenharmony_ci	list_for_each_safe(tmp, next, q) {
11688c2ecf20Sopenharmony_ci		ioreq = (struct csio_ioreq *)tmp;
11698c2ecf20Sopenharmony_ci		csio_scsi_drvcleanup(ioreq);
11708c2ecf20Sopenharmony_ci		list_del_init(&ioreq->sm.sm_list);
11718c2ecf20Sopenharmony_ci		scmnd = csio_scsi_cmnd(ioreq);
11728c2ecf20Sopenharmony_ci		spin_unlock_irq(&hw->lock);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci		/*
11758c2ecf20Sopenharmony_ci		 * Upper layers may have cleared this command, hence this
11768c2ecf20Sopenharmony_ci		 * check to avoid accessing stale references.
11778c2ecf20Sopenharmony_ci		 */
11788c2ecf20Sopenharmony_ci		if (scmnd != NULL)
11798c2ecf20Sopenharmony_ci			ioreq->io_cbfn(hw, ioreq);
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci		spin_lock_irq(&scm->freelist_lock);
11828c2ecf20Sopenharmony_ci		csio_put_scsi_ioreq(scm, ioreq);
11838c2ecf20Sopenharmony_ci		spin_unlock_irq(&scm->freelist_lock);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci		spin_lock_irq(&hw->lock);
11868c2ecf20Sopenharmony_ci	}
11878c2ecf20Sopenharmony_ci}
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci#define CSIO_SCSI_ABORT_Q_POLL_MS		2000
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_cistatic void
11928c2ecf20Sopenharmony_cicsio_abrt_cls(struct csio_ioreq *ioreq, struct scsi_cmnd *scmnd)
11938c2ecf20Sopenharmony_ci{
11948c2ecf20Sopenharmony_ci	struct csio_lnode *ln = ioreq->lnode;
11958c2ecf20Sopenharmony_ci	struct csio_hw *hw = ln->hwp;
11968c2ecf20Sopenharmony_ci	int ready = 0;
11978c2ecf20Sopenharmony_ci	struct csio_scsim *scsim = csio_hw_to_scsim(hw);
11988c2ecf20Sopenharmony_ci	int rv;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	if (csio_scsi_cmnd(ioreq) != scmnd) {
12018c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scsim, n_abrt_race_comp);
12028c2ecf20Sopenharmony_ci		return;
12038c2ecf20Sopenharmony_ci	}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	ready = csio_is_lnode_ready(ln);
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	rv = csio_do_abrt_cls(hw, ioreq, (ready ? SCSI_ABORT : SCSI_CLOSE));
12088c2ecf20Sopenharmony_ci	if (rv != 0) {
12098c2ecf20Sopenharmony_ci		if (ready)
12108c2ecf20Sopenharmony_ci			CSIO_INC_STATS(scsim, n_abrt_busy_error);
12118c2ecf20Sopenharmony_ci		else
12128c2ecf20Sopenharmony_ci			CSIO_INC_STATS(scsim, n_cls_busy_error);
12138c2ecf20Sopenharmony_ci	}
12148c2ecf20Sopenharmony_ci}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci/*
12178c2ecf20Sopenharmony_ci * csio_scsi_abort_io_q - Abort all I/Os on given queue
12188c2ecf20Sopenharmony_ci * @scm: SCSI module.
12198c2ecf20Sopenharmony_ci * @q: Queue to abort.
12208c2ecf20Sopenharmony_ci * @tmo: Timeout in ms
12218c2ecf20Sopenharmony_ci *
12228c2ecf20Sopenharmony_ci * Attempt to abort all I/Os on given queue, and wait for a max
12238c2ecf20Sopenharmony_ci * of tmo milliseconds for them to complete. Returns success
12248c2ecf20Sopenharmony_ci * if all I/Os are aborted. Else returns -ETIMEDOUT.
12258c2ecf20Sopenharmony_ci * Should be entered with lock held. Exits with lock held.
12268c2ecf20Sopenharmony_ci * NOTE:
12278c2ecf20Sopenharmony_ci * Lock has to be held across the loop that aborts I/Os, since dropping the lock
12288c2ecf20Sopenharmony_ci * in between can cause the list to be corrupted. As a result, the caller
12298c2ecf20Sopenharmony_ci * of this function has to ensure that the number of I/os to be aborted
12308c2ecf20Sopenharmony_ci * is finite enough to not cause lock-held-for-too-long issues.
12318c2ecf20Sopenharmony_ci */
12328c2ecf20Sopenharmony_cistatic int
12338c2ecf20Sopenharmony_cicsio_scsi_abort_io_q(struct csio_scsim *scm, struct list_head *q, uint32_t tmo)
12348c2ecf20Sopenharmony_ci{
12358c2ecf20Sopenharmony_ci	struct csio_hw *hw = scm->hw;
12368c2ecf20Sopenharmony_ci	struct list_head *tmp, *next;
12378c2ecf20Sopenharmony_ci	int count = DIV_ROUND_UP(tmo, CSIO_SCSI_ABORT_Q_POLL_MS);
12388c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmnd;
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	if (list_empty(q))
12418c2ecf20Sopenharmony_ci		return 0;
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	csio_dbg(hw, "Aborting SCSI I/Os\n");
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	/* Now abort/close I/Os in the queue passed */
12468c2ecf20Sopenharmony_ci	list_for_each_safe(tmp, next, q) {
12478c2ecf20Sopenharmony_ci		scmnd = csio_scsi_cmnd((struct csio_ioreq *)tmp);
12488c2ecf20Sopenharmony_ci		csio_abrt_cls((struct csio_ioreq *)tmp, scmnd);
12498c2ecf20Sopenharmony_ci	}
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	/* Wait till all active I/Os are completed/aborted/closed */
12528c2ecf20Sopenharmony_ci	while (!list_empty(q) && count--) {
12538c2ecf20Sopenharmony_ci		spin_unlock_irq(&hw->lock);
12548c2ecf20Sopenharmony_ci		msleep(CSIO_SCSI_ABORT_Q_POLL_MS);
12558c2ecf20Sopenharmony_ci		spin_lock_irq(&hw->lock);
12568c2ecf20Sopenharmony_ci	}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	/* all aborts completed */
12598c2ecf20Sopenharmony_ci	if (list_empty(q))
12608c2ecf20Sopenharmony_ci		return 0;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
12638c2ecf20Sopenharmony_ci}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci/*
12668c2ecf20Sopenharmony_ci * csio_scsim_cleanup_io - Cleanup all I/Os in SCSI module.
12678c2ecf20Sopenharmony_ci * @scm: SCSI module.
12688c2ecf20Sopenharmony_ci * @abort: abort required.
12698c2ecf20Sopenharmony_ci * Called with lock held, should exit with lock held.
12708c2ecf20Sopenharmony_ci * Can sleep when waiting for I/Os to complete.
12718c2ecf20Sopenharmony_ci */
12728c2ecf20Sopenharmony_ciint
12738c2ecf20Sopenharmony_cicsio_scsim_cleanup_io(struct csio_scsim *scm, bool abort)
12748c2ecf20Sopenharmony_ci{
12758c2ecf20Sopenharmony_ci	struct csio_hw *hw = scm->hw;
12768c2ecf20Sopenharmony_ci	int rv = 0;
12778c2ecf20Sopenharmony_ci	int count = DIV_ROUND_UP(60 * 1000, CSIO_SCSI_ABORT_Q_POLL_MS);
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	/* No I/Os pending */
12808c2ecf20Sopenharmony_ci	if (list_empty(&scm->active_q))
12818c2ecf20Sopenharmony_ci		return 0;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	/* Wait until all active I/Os are completed */
12848c2ecf20Sopenharmony_ci	while (!list_empty(&scm->active_q) && count--) {
12858c2ecf20Sopenharmony_ci		spin_unlock_irq(&hw->lock);
12868c2ecf20Sopenharmony_ci		msleep(CSIO_SCSI_ABORT_Q_POLL_MS);
12878c2ecf20Sopenharmony_ci		spin_lock_irq(&hw->lock);
12888c2ecf20Sopenharmony_ci	}
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	/* all I/Os completed */
12918c2ecf20Sopenharmony_ci	if (list_empty(&scm->active_q))
12928c2ecf20Sopenharmony_ci		return 0;
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	/* Else abort */
12958c2ecf20Sopenharmony_ci	if (abort) {
12968c2ecf20Sopenharmony_ci		rv = csio_scsi_abort_io_q(scm, &scm->active_q, 30000);
12978c2ecf20Sopenharmony_ci		if (rv == 0)
12988c2ecf20Sopenharmony_ci			return rv;
12998c2ecf20Sopenharmony_ci		csio_dbg(hw, "Some I/O aborts timed out, cleaning up..\n");
13008c2ecf20Sopenharmony_ci	}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	csio_scsi_cleanup_io_q(scm, &scm->active_q);
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	CSIO_DB_ASSERT(list_empty(&scm->active_q));
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	return rv;
13078c2ecf20Sopenharmony_ci}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci/*
13108c2ecf20Sopenharmony_ci * csio_scsim_cleanup_io_lnode - Cleanup all I/Os of given lnode.
13118c2ecf20Sopenharmony_ci * @scm: SCSI module.
13128c2ecf20Sopenharmony_ci * @lnode: lnode
13138c2ecf20Sopenharmony_ci *
13148c2ecf20Sopenharmony_ci * Called with lock held, should exit with lock held.
13158c2ecf20Sopenharmony_ci * Can sleep (with dropped lock) when waiting for I/Os to complete.
13168c2ecf20Sopenharmony_ci */
13178c2ecf20Sopenharmony_ciint
13188c2ecf20Sopenharmony_cicsio_scsim_cleanup_io_lnode(struct csio_scsim *scm, struct csio_lnode *ln)
13198c2ecf20Sopenharmony_ci{
13208c2ecf20Sopenharmony_ci	struct csio_hw *hw = scm->hw;
13218c2ecf20Sopenharmony_ci	struct csio_scsi_level_data sld;
13228c2ecf20Sopenharmony_ci	int rv;
13238c2ecf20Sopenharmony_ci	int count = DIV_ROUND_UP(60 * 1000, CSIO_SCSI_ABORT_Q_POLL_MS);
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	csio_dbg(hw, "Gathering all SCSI I/Os on lnode %p\n", ln);
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	sld.level = CSIO_LEV_LNODE;
13288c2ecf20Sopenharmony_ci	sld.lnode = ln;
13298c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ln->cmpl_q);
13308c2ecf20Sopenharmony_ci	csio_scsi_gather_active_ios(scm, &sld, &ln->cmpl_q);
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	/* No I/Os pending on this lnode  */
13338c2ecf20Sopenharmony_ci	if (list_empty(&ln->cmpl_q))
13348c2ecf20Sopenharmony_ci		return 0;
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	/* Wait until all active I/Os on this lnode are completed */
13378c2ecf20Sopenharmony_ci	while (!list_empty(&ln->cmpl_q) && count--) {
13388c2ecf20Sopenharmony_ci		spin_unlock_irq(&hw->lock);
13398c2ecf20Sopenharmony_ci		msleep(CSIO_SCSI_ABORT_Q_POLL_MS);
13408c2ecf20Sopenharmony_ci		spin_lock_irq(&hw->lock);
13418c2ecf20Sopenharmony_ci	}
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	/* all I/Os completed */
13448c2ecf20Sopenharmony_ci	if (list_empty(&ln->cmpl_q))
13458c2ecf20Sopenharmony_ci		return 0;
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	csio_dbg(hw, "Some I/Os pending on ln:%p, aborting them..\n", ln);
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	/* I/Os are pending, abort them */
13508c2ecf20Sopenharmony_ci	rv = csio_scsi_abort_io_q(scm, &ln->cmpl_q, 30000);
13518c2ecf20Sopenharmony_ci	if (rv != 0) {
13528c2ecf20Sopenharmony_ci		csio_dbg(hw, "Some I/O aborts timed out, cleaning up..\n");
13538c2ecf20Sopenharmony_ci		csio_scsi_cleanup_io_q(scm, &ln->cmpl_q);
13548c2ecf20Sopenharmony_ci	}
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	CSIO_DB_ASSERT(list_empty(&ln->cmpl_q));
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	return rv;
13598c2ecf20Sopenharmony_ci}
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_cistatic ssize_t
13628c2ecf20Sopenharmony_cicsio_show_hw_state(struct device *dev,
13638c2ecf20Sopenharmony_ci		   struct device_attribute *attr, char *buf)
13648c2ecf20Sopenharmony_ci{
13658c2ecf20Sopenharmony_ci	struct csio_lnode *ln = shost_priv(class_to_shost(dev));
13668c2ecf20Sopenharmony_ci	struct csio_hw *hw = csio_lnode_to_hw(ln);
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	if (csio_is_hw_ready(hw))
13698c2ecf20Sopenharmony_ci		return snprintf(buf, PAGE_SIZE, "ready\n");
13708c2ecf20Sopenharmony_ci	else
13718c2ecf20Sopenharmony_ci		return snprintf(buf, PAGE_SIZE, "not ready\n");
13728c2ecf20Sopenharmony_ci}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci/* Device reset */
13758c2ecf20Sopenharmony_cistatic ssize_t
13768c2ecf20Sopenharmony_cicsio_device_reset(struct device *dev,
13778c2ecf20Sopenharmony_ci		   struct device_attribute *attr, const char *buf, size_t count)
13788c2ecf20Sopenharmony_ci{
13798c2ecf20Sopenharmony_ci	struct csio_lnode *ln = shost_priv(class_to_shost(dev));
13808c2ecf20Sopenharmony_ci	struct csio_hw *hw = csio_lnode_to_hw(ln);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	if (*buf != '1')
13838c2ecf20Sopenharmony_ci		return -EINVAL;
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	/* Delete NPIV lnodes */
13868c2ecf20Sopenharmony_ci	csio_lnodes_exit(hw, 1);
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	/* Block upper IOs */
13898c2ecf20Sopenharmony_ci	csio_lnodes_block_request(hw);
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	spin_lock_irq(&hw->lock);
13928c2ecf20Sopenharmony_ci	csio_hw_reset(hw);
13938c2ecf20Sopenharmony_ci	spin_unlock_irq(&hw->lock);
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	/* Unblock upper IOs */
13968c2ecf20Sopenharmony_ci	csio_lnodes_unblock_request(hw);
13978c2ecf20Sopenharmony_ci	return count;
13988c2ecf20Sopenharmony_ci}
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci/* disable port */
14018c2ecf20Sopenharmony_cistatic ssize_t
14028c2ecf20Sopenharmony_cicsio_disable_port(struct device *dev,
14038c2ecf20Sopenharmony_ci		   struct device_attribute *attr, const char *buf, size_t count)
14048c2ecf20Sopenharmony_ci{
14058c2ecf20Sopenharmony_ci	struct csio_lnode *ln = shost_priv(class_to_shost(dev));
14068c2ecf20Sopenharmony_ci	struct csio_hw *hw = csio_lnode_to_hw(ln);
14078c2ecf20Sopenharmony_ci	bool disable;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	if (*buf == '1' || *buf == '0')
14108c2ecf20Sopenharmony_ci		disable = (*buf == '1') ? true : false;
14118c2ecf20Sopenharmony_ci	else
14128c2ecf20Sopenharmony_ci		return -EINVAL;
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	/* Block upper IOs */
14158c2ecf20Sopenharmony_ci	csio_lnodes_block_by_port(hw, ln->portid);
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	spin_lock_irq(&hw->lock);
14188c2ecf20Sopenharmony_ci	csio_disable_lnodes(hw, ln->portid, disable);
14198c2ecf20Sopenharmony_ci	spin_unlock_irq(&hw->lock);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	/* Unblock upper IOs */
14228c2ecf20Sopenharmony_ci	csio_lnodes_unblock_by_port(hw, ln->portid);
14238c2ecf20Sopenharmony_ci	return count;
14248c2ecf20Sopenharmony_ci}
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci/* Show debug level */
14278c2ecf20Sopenharmony_cistatic ssize_t
14288c2ecf20Sopenharmony_cicsio_show_dbg_level(struct device *dev,
14298c2ecf20Sopenharmony_ci		   struct device_attribute *attr, char *buf)
14308c2ecf20Sopenharmony_ci{
14318c2ecf20Sopenharmony_ci	struct csio_lnode *ln = shost_priv(class_to_shost(dev));
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%x\n", ln->params.log_level);
14348c2ecf20Sopenharmony_ci}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci/* Store debug level */
14378c2ecf20Sopenharmony_cistatic ssize_t
14388c2ecf20Sopenharmony_cicsio_store_dbg_level(struct device *dev,
14398c2ecf20Sopenharmony_ci		   struct device_attribute *attr, const char *buf, size_t count)
14408c2ecf20Sopenharmony_ci{
14418c2ecf20Sopenharmony_ci	struct csio_lnode *ln = shost_priv(class_to_shost(dev));
14428c2ecf20Sopenharmony_ci	struct csio_hw *hw = csio_lnode_to_hw(ln);
14438c2ecf20Sopenharmony_ci	uint32_t dbg_level = 0;
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	if (!isdigit(buf[0]))
14468c2ecf20Sopenharmony_ci		return -EINVAL;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	if (sscanf(buf, "%i", &dbg_level))
14498c2ecf20Sopenharmony_ci		return -EINVAL;
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	ln->params.log_level = dbg_level;
14528c2ecf20Sopenharmony_ci	hw->params.log_level = dbg_level;
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	return 0;
14558c2ecf20Sopenharmony_ci}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_cistatic DEVICE_ATTR(hw_state, S_IRUGO, csio_show_hw_state, NULL);
14588c2ecf20Sopenharmony_cistatic DEVICE_ATTR(device_reset, S_IWUSR, NULL, csio_device_reset);
14598c2ecf20Sopenharmony_cistatic DEVICE_ATTR(disable_port, S_IWUSR, NULL, csio_disable_port);
14608c2ecf20Sopenharmony_cistatic DEVICE_ATTR(dbg_level, S_IRUGO | S_IWUSR, csio_show_dbg_level,
14618c2ecf20Sopenharmony_ci		  csio_store_dbg_level);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_cistatic struct device_attribute *csio_fcoe_lport_attrs[] = {
14648c2ecf20Sopenharmony_ci	&dev_attr_hw_state,
14658c2ecf20Sopenharmony_ci	&dev_attr_device_reset,
14668c2ecf20Sopenharmony_ci	&dev_attr_disable_port,
14678c2ecf20Sopenharmony_ci	&dev_attr_dbg_level,
14688c2ecf20Sopenharmony_ci	NULL,
14698c2ecf20Sopenharmony_ci};
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_cistatic ssize_t
14728c2ecf20Sopenharmony_cicsio_show_num_reg_rnodes(struct device *dev,
14738c2ecf20Sopenharmony_ci		     struct device_attribute *attr, char *buf)
14748c2ecf20Sopenharmony_ci{
14758c2ecf20Sopenharmony_ci	struct csio_lnode *ln = shost_priv(class_to_shost(dev));
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", ln->num_reg_rnodes);
14788c2ecf20Sopenharmony_ci}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_cistatic DEVICE_ATTR(num_reg_rnodes, S_IRUGO, csio_show_num_reg_rnodes, NULL);
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_cistatic struct device_attribute *csio_fcoe_vport_attrs[] = {
14838c2ecf20Sopenharmony_ci	&dev_attr_num_reg_rnodes,
14848c2ecf20Sopenharmony_ci	&dev_attr_dbg_level,
14858c2ecf20Sopenharmony_ci	NULL,
14868c2ecf20Sopenharmony_ci};
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_cistatic inline uint32_t
14898c2ecf20Sopenharmony_cicsio_scsi_copy_to_sgl(struct csio_hw *hw, struct csio_ioreq *req)
14908c2ecf20Sopenharmony_ci{
14918c2ecf20Sopenharmony_ci	struct scsi_cmnd *scmnd  = (struct scsi_cmnd *)csio_scsi_cmnd(req);
14928c2ecf20Sopenharmony_ci	struct scatterlist *sg;
14938c2ecf20Sopenharmony_ci	uint32_t bytes_left;
14948c2ecf20Sopenharmony_ci	uint32_t bytes_copy;
14958c2ecf20Sopenharmony_ci	uint32_t buf_off = 0;
14968c2ecf20Sopenharmony_ci	uint32_t start_off = 0;
14978c2ecf20Sopenharmony_ci	uint32_t sg_off = 0;
14988c2ecf20Sopenharmony_ci	void *sg_addr;
14998c2ecf20Sopenharmony_ci	void *buf_addr;
15008c2ecf20Sopenharmony_ci	struct csio_dma_buf *dma_buf;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	bytes_left = scsi_bufflen(scmnd);
15038c2ecf20Sopenharmony_ci	sg = scsi_sglist(scmnd);
15048c2ecf20Sopenharmony_ci	dma_buf = (struct csio_dma_buf *)csio_list_next(&req->gen_list);
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	/* Copy data from driver buffer to SGs of SCSI CMD */
15078c2ecf20Sopenharmony_ci	while (bytes_left > 0 && sg && dma_buf) {
15088c2ecf20Sopenharmony_ci		if (buf_off >= dma_buf->len) {
15098c2ecf20Sopenharmony_ci			buf_off = 0;
15108c2ecf20Sopenharmony_ci			dma_buf = (struct csio_dma_buf *)
15118c2ecf20Sopenharmony_ci					csio_list_next(dma_buf);
15128c2ecf20Sopenharmony_ci			continue;
15138c2ecf20Sopenharmony_ci		}
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci		if (start_off >= sg->length) {
15168c2ecf20Sopenharmony_ci			start_off -= sg->length;
15178c2ecf20Sopenharmony_ci			sg = sg_next(sg);
15188c2ecf20Sopenharmony_ci			continue;
15198c2ecf20Sopenharmony_ci		}
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci		buf_addr = dma_buf->vaddr + buf_off;
15228c2ecf20Sopenharmony_ci		sg_off = sg->offset + start_off;
15238c2ecf20Sopenharmony_ci		bytes_copy = min((dma_buf->len - buf_off),
15248c2ecf20Sopenharmony_ci				sg->length - start_off);
15258c2ecf20Sopenharmony_ci		bytes_copy = min((uint32_t)(PAGE_SIZE - (sg_off & ~PAGE_MASK)),
15268c2ecf20Sopenharmony_ci				 bytes_copy);
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci		sg_addr = kmap_atomic(sg_page(sg) + (sg_off >> PAGE_SHIFT));
15298c2ecf20Sopenharmony_ci		if (!sg_addr) {
15308c2ecf20Sopenharmony_ci			csio_err(hw, "failed to kmap sg:%p of ioreq:%p\n",
15318c2ecf20Sopenharmony_ci				sg, req);
15328c2ecf20Sopenharmony_ci			break;
15338c2ecf20Sopenharmony_ci		}
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci		csio_dbg(hw, "copy_to_sgl:sg_addr %p sg_off %d buf %p len %d\n",
15368c2ecf20Sopenharmony_ci				sg_addr, sg_off, buf_addr, bytes_copy);
15378c2ecf20Sopenharmony_ci		memcpy(sg_addr + (sg_off & ~PAGE_MASK), buf_addr, bytes_copy);
15388c2ecf20Sopenharmony_ci		kunmap_atomic(sg_addr);
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci		start_off +=  bytes_copy;
15418c2ecf20Sopenharmony_ci		buf_off += bytes_copy;
15428c2ecf20Sopenharmony_ci		bytes_left -= bytes_copy;
15438c2ecf20Sopenharmony_ci	}
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	if (bytes_left > 0)
15468c2ecf20Sopenharmony_ci		return DID_ERROR;
15478c2ecf20Sopenharmony_ci	else
15488c2ecf20Sopenharmony_ci		return DID_OK;
15498c2ecf20Sopenharmony_ci}
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci/*
15528c2ecf20Sopenharmony_ci * csio_scsi_err_handler - SCSI error handler.
15538c2ecf20Sopenharmony_ci * @hw: HW module.
15548c2ecf20Sopenharmony_ci * @req: IO request.
15558c2ecf20Sopenharmony_ci *
15568c2ecf20Sopenharmony_ci */
15578c2ecf20Sopenharmony_cistatic inline void
15588c2ecf20Sopenharmony_cicsio_scsi_err_handler(struct csio_hw *hw, struct csio_ioreq *req)
15598c2ecf20Sopenharmony_ci{
15608c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmnd  = (struct scsi_cmnd *)csio_scsi_cmnd(req);
15618c2ecf20Sopenharmony_ci	struct csio_scsim *scm = csio_hw_to_scsim(hw);
15628c2ecf20Sopenharmony_ci	struct fcp_resp_with_ext *fcp_resp;
15638c2ecf20Sopenharmony_ci	struct fcp_resp_rsp_info *rsp_info;
15648c2ecf20Sopenharmony_ci	struct csio_dma_buf *dma_buf;
15658c2ecf20Sopenharmony_ci	uint8_t flags, scsi_status = 0;
15668c2ecf20Sopenharmony_ci	uint32_t host_status = DID_OK;
15678c2ecf20Sopenharmony_ci	uint32_t rsp_len = 0, sns_len = 0;
15688c2ecf20Sopenharmony_ci	struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata);
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci	switch (req->wr_status) {
15728c2ecf20Sopenharmony_ci	case FW_HOSTERROR:
15738c2ecf20Sopenharmony_ci		if (unlikely(!csio_is_hw_ready(hw)))
15748c2ecf20Sopenharmony_ci			return;
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
15778c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_hosterror);
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci		break;
15808c2ecf20Sopenharmony_ci	case FW_SCSI_RSP_ERR:
15818c2ecf20Sopenharmony_ci		dma_buf = &req->dma_buf;
15828c2ecf20Sopenharmony_ci		fcp_resp = (struct fcp_resp_with_ext *)dma_buf->vaddr;
15838c2ecf20Sopenharmony_ci		rsp_info = (struct fcp_resp_rsp_info *)(fcp_resp + 1);
15848c2ecf20Sopenharmony_ci		flags = fcp_resp->resp.fr_flags;
15858c2ecf20Sopenharmony_ci		scsi_status = fcp_resp->resp.fr_status;
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci		if (flags & FCP_RSP_LEN_VAL) {
15888c2ecf20Sopenharmony_ci			rsp_len = be32_to_cpu(fcp_resp->ext.fr_rsp_len);
15898c2ecf20Sopenharmony_ci			if ((rsp_len != 0 && rsp_len != 4 && rsp_len != 8) ||
15908c2ecf20Sopenharmony_ci				(rsp_info->rsp_code != FCP_TMF_CMPL)) {
15918c2ecf20Sopenharmony_ci				host_status = DID_ERROR;
15928c2ecf20Sopenharmony_ci				goto out;
15938c2ecf20Sopenharmony_ci			}
15948c2ecf20Sopenharmony_ci		}
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci		if ((flags & FCP_SNS_LEN_VAL) && fcp_resp->ext.fr_sns_len) {
15978c2ecf20Sopenharmony_ci			sns_len = be32_to_cpu(fcp_resp->ext.fr_sns_len);
15988c2ecf20Sopenharmony_ci			if (sns_len > SCSI_SENSE_BUFFERSIZE)
15998c2ecf20Sopenharmony_ci				sns_len = SCSI_SENSE_BUFFERSIZE;
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci			memcpy(cmnd->sense_buffer,
16028c2ecf20Sopenharmony_ci			       &rsp_info->_fr_resvd[0] + rsp_len, sns_len);
16038c2ecf20Sopenharmony_ci			CSIO_INC_STATS(scm, n_autosense);
16048c2ecf20Sopenharmony_ci		}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci		scsi_set_resid(cmnd, 0);
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci		/* Under run */
16098c2ecf20Sopenharmony_ci		if (flags & FCP_RESID_UNDER) {
16108c2ecf20Sopenharmony_ci			scsi_set_resid(cmnd,
16118c2ecf20Sopenharmony_ci				       be32_to_cpu(fcp_resp->ext.fr_resid));
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci			if (!(flags & FCP_SNS_LEN_VAL) &&
16148c2ecf20Sopenharmony_ci			    (scsi_status == SAM_STAT_GOOD) &&
16158c2ecf20Sopenharmony_ci			    ((scsi_bufflen(cmnd) - scsi_get_resid(cmnd))
16168c2ecf20Sopenharmony_ci							< cmnd->underflow))
16178c2ecf20Sopenharmony_ci				host_status = DID_ERROR;
16188c2ecf20Sopenharmony_ci		} else if (flags & FCP_RESID_OVER)
16198c2ecf20Sopenharmony_ci			host_status = DID_ERROR;
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_rsperror);
16228c2ecf20Sopenharmony_ci		break;
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	case FW_SCSI_OVER_FLOW_ERR:
16258c2ecf20Sopenharmony_ci		csio_warn(hw,
16268c2ecf20Sopenharmony_ci			  "Over-flow error,cmnd:0x%x expected len:0x%x"
16278c2ecf20Sopenharmony_ci			  " resid:0x%x\n", cmnd->cmnd[0],
16288c2ecf20Sopenharmony_ci			  scsi_bufflen(cmnd), scsi_get_resid(cmnd));
16298c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
16308c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_ovflerror);
16318c2ecf20Sopenharmony_ci		break;
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	case FW_SCSI_UNDER_FLOW_ERR:
16348c2ecf20Sopenharmony_ci		csio_warn(hw,
16358c2ecf20Sopenharmony_ci			  "Under-flow error,cmnd:0x%x expected"
16368c2ecf20Sopenharmony_ci			  " len:0x%x resid:0x%x lun:0x%llx ssn:0x%x\n",
16378c2ecf20Sopenharmony_ci			  cmnd->cmnd[0], scsi_bufflen(cmnd),
16388c2ecf20Sopenharmony_ci			  scsi_get_resid(cmnd), cmnd->device->lun,
16398c2ecf20Sopenharmony_ci			  rn->flowid);
16408c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
16418c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_unflerror);
16428c2ecf20Sopenharmony_ci		break;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	case FW_SCSI_ABORT_REQUESTED:
16458c2ecf20Sopenharmony_ci	case FW_SCSI_ABORTED:
16468c2ecf20Sopenharmony_ci	case FW_SCSI_CLOSE_REQUESTED:
16478c2ecf20Sopenharmony_ci		csio_dbg(hw, "Req %p cmd:%p op:%x %s\n", req, cmnd,
16488c2ecf20Sopenharmony_ci			     cmnd->cmnd[0],
16498c2ecf20Sopenharmony_ci			    (req->wr_status == FW_SCSI_CLOSE_REQUESTED) ?
16508c2ecf20Sopenharmony_ci			    "closed" : "aborted");
16518c2ecf20Sopenharmony_ci		/*
16528c2ecf20Sopenharmony_ci		 * csio_eh_abort_handler checks this value to
16538c2ecf20Sopenharmony_ci		 * succeed or fail the abort request.
16548c2ecf20Sopenharmony_ci		 */
16558c2ecf20Sopenharmony_ci		host_status = DID_REQUEUE;
16568c2ecf20Sopenharmony_ci		if (req->wr_status == FW_SCSI_CLOSE_REQUESTED)
16578c2ecf20Sopenharmony_ci			CSIO_INC_STATS(scm, n_closed);
16588c2ecf20Sopenharmony_ci		else
16598c2ecf20Sopenharmony_ci			CSIO_INC_STATS(scm, n_aborted);
16608c2ecf20Sopenharmony_ci		break;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	case FW_SCSI_ABORT_TIMEDOUT:
16638c2ecf20Sopenharmony_ci		/* FW timed out the abort itself */
16648c2ecf20Sopenharmony_ci		csio_dbg(hw, "FW timed out abort req:%p cmnd:%p status:%x\n",
16658c2ecf20Sopenharmony_ci			 req, cmnd, req->wr_status);
16668c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
16678c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_abrt_timedout);
16688c2ecf20Sopenharmony_ci		break;
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	case FW_RDEV_NOT_READY:
16718c2ecf20Sopenharmony_ci		/*
16728c2ecf20Sopenharmony_ci		 * In firmware, a RDEV can get into this state
16738c2ecf20Sopenharmony_ci		 * temporarily, before moving into dissapeared/lost
16748c2ecf20Sopenharmony_ci		 * state. So, the driver should complete the request equivalent
16758c2ecf20Sopenharmony_ci		 * to device-disappeared!
16768c2ecf20Sopenharmony_ci		 */
16778c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_rdev_nr_error);
16788c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
16798c2ecf20Sopenharmony_ci		break;
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	case FW_ERR_RDEV_LOST:
16828c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_rdev_lost_error);
16838c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
16848c2ecf20Sopenharmony_ci		break;
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	case FW_ERR_RDEV_LOGO:
16878c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_rdev_logo_error);
16888c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
16898c2ecf20Sopenharmony_ci		break;
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci	case FW_ERR_RDEV_IMPL_LOGO:
16928c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
16938c2ecf20Sopenharmony_ci		break;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	case FW_ERR_LINK_DOWN:
16968c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_link_down_error);
16978c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
16988c2ecf20Sopenharmony_ci		break;
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	case FW_FCOE_NO_XCHG:
17018c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_no_xchg_error);
17028c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
17038c2ecf20Sopenharmony_ci		break;
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci	default:
17068c2ecf20Sopenharmony_ci		csio_err(hw, "Unknown SCSI FW WR status:%d req:%p cmnd:%p\n",
17078c2ecf20Sopenharmony_ci			    req->wr_status, req, cmnd);
17088c2ecf20Sopenharmony_ci		CSIO_DB_ASSERT(0);
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_unknown_error);
17118c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
17128c2ecf20Sopenharmony_ci		break;
17138c2ecf20Sopenharmony_ci	}
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ciout:
17168c2ecf20Sopenharmony_ci	if (req->nsge > 0) {
17178c2ecf20Sopenharmony_ci		scsi_dma_unmap(cmnd);
17188c2ecf20Sopenharmony_ci		if (req->dcopy && (host_status == DID_OK))
17198c2ecf20Sopenharmony_ci			host_status = csio_scsi_copy_to_sgl(hw, req);
17208c2ecf20Sopenharmony_ci	}
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	cmnd->result = (((host_status) << 16) | scsi_status);
17238c2ecf20Sopenharmony_ci	cmnd->scsi_done(cmnd);
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	/* Wake up waiting threads */
17268c2ecf20Sopenharmony_ci	csio_scsi_cmnd(req) = NULL;
17278c2ecf20Sopenharmony_ci	complete(&req->cmplobj);
17288c2ecf20Sopenharmony_ci}
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci/*
17318c2ecf20Sopenharmony_ci * csio_scsi_cbfn - SCSI callback function.
17328c2ecf20Sopenharmony_ci * @hw: HW module.
17338c2ecf20Sopenharmony_ci * @req: IO request.
17348c2ecf20Sopenharmony_ci *
17358c2ecf20Sopenharmony_ci */
17368c2ecf20Sopenharmony_cistatic void
17378c2ecf20Sopenharmony_cicsio_scsi_cbfn(struct csio_hw *hw, struct csio_ioreq *req)
17388c2ecf20Sopenharmony_ci{
17398c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmnd  = (struct scsi_cmnd *)csio_scsi_cmnd(req);
17408c2ecf20Sopenharmony_ci	uint8_t scsi_status = SAM_STAT_GOOD;
17418c2ecf20Sopenharmony_ci	uint32_t host_status = DID_OK;
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	if (likely(req->wr_status == FW_SUCCESS)) {
17448c2ecf20Sopenharmony_ci		if (req->nsge > 0) {
17458c2ecf20Sopenharmony_ci			scsi_dma_unmap(cmnd);
17468c2ecf20Sopenharmony_ci			if (req->dcopy)
17478c2ecf20Sopenharmony_ci				host_status = csio_scsi_copy_to_sgl(hw, req);
17488c2ecf20Sopenharmony_ci		}
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci		cmnd->result = (((host_status) << 16) | scsi_status);
17518c2ecf20Sopenharmony_ci		cmnd->scsi_done(cmnd);
17528c2ecf20Sopenharmony_ci		csio_scsi_cmnd(req) = NULL;
17538c2ecf20Sopenharmony_ci		CSIO_INC_STATS(csio_hw_to_scsim(hw), n_tot_success);
17548c2ecf20Sopenharmony_ci	} else {
17558c2ecf20Sopenharmony_ci		/* Error handling */
17568c2ecf20Sopenharmony_ci		csio_scsi_err_handler(hw, req);
17578c2ecf20Sopenharmony_ci	}
17588c2ecf20Sopenharmony_ci}
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci/**
17618c2ecf20Sopenharmony_ci * csio_queuecommand - Entry point to kickstart an I/O request.
17628c2ecf20Sopenharmony_ci * @host:	The scsi_host pointer.
17638c2ecf20Sopenharmony_ci * @cmnd:	The I/O request from ML.
17648c2ecf20Sopenharmony_ci *
17658c2ecf20Sopenharmony_ci * This routine does the following:
17668c2ecf20Sopenharmony_ci *	- Checks for HW and Rnode module readiness.
17678c2ecf20Sopenharmony_ci *	- Gets a free ioreq structure (which is already initialized
17688c2ecf20Sopenharmony_ci *	  to uninit during its allocation).
17698c2ecf20Sopenharmony_ci *	- Maps SG elements.
17708c2ecf20Sopenharmony_ci *	- Initializes ioreq members.
17718c2ecf20Sopenharmony_ci *	- Kicks off the SCSI state machine for this IO.
17728c2ecf20Sopenharmony_ci *	- Returns busy status on error.
17738c2ecf20Sopenharmony_ci */
17748c2ecf20Sopenharmony_cistatic int
17758c2ecf20Sopenharmony_cicsio_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmnd)
17768c2ecf20Sopenharmony_ci{
17778c2ecf20Sopenharmony_ci	struct csio_lnode *ln = shost_priv(host);
17788c2ecf20Sopenharmony_ci	struct csio_hw *hw = csio_lnode_to_hw(ln);
17798c2ecf20Sopenharmony_ci	struct csio_scsim *scsim = csio_hw_to_scsim(hw);
17808c2ecf20Sopenharmony_ci	struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata);
17818c2ecf20Sopenharmony_ci	struct csio_ioreq *ioreq = NULL;
17828c2ecf20Sopenharmony_ci	unsigned long flags;
17838c2ecf20Sopenharmony_ci	int nsge = 0;
17848c2ecf20Sopenharmony_ci	int rv = SCSI_MLQUEUE_HOST_BUSY, nr;
17858c2ecf20Sopenharmony_ci	int retval;
17868c2ecf20Sopenharmony_ci	struct csio_scsi_qset *sqset;
17878c2ecf20Sopenharmony_ci	struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	sqset = &hw->sqset[ln->portid][blk_mq_rq_cpu(cmnd->request)];
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	nr = fc_remote_port_chkready(rport);
17928c2ecf20Sopenharmony_ci	if (nr) {
17938c2ecf20Sopenharmony_ci		cmnd->result = nr;
17948c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scsim, n_rn_nr_error);
17958c2ecf20Sopenharmony_ci		goto err_done;
17968c2ecf20Sopenharmony_ci	}
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci	if (unlikely(!csio_is_hw_ready(hw))) {
17998c2ecf20Sopenharmony_ci		cmnd->result = (DID_REQUEUE << 16);
18008c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scsim, n_hw_nr_error);
18018c2ecf20Sopenharmony_ci		goto err_done;
18028c2ecf20Sopenharmony_ci	}
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	/* Get req->nsge, if there are SG elements to be mapped  */
18058c2ecf20Sopenharmony_ci	nsge = scsi_dma_map(cmnd);
18068c2ecf20Sopenharmony_ci	if (unlikely(nsge < 0)) {
18078c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scsim, n_dmamap_error);
18088c2ecf20Sopenharmony_ci		goto err;
18098c2ecf20Sopenharmony_ci	}
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_ci	/* Do we support so many mappings? */
18128c2ecf20Sopenharmony_ci	if (unlikely(nsge > scsim->max_sge)) {
18138c2ecf20Sopenharmony_ci		csio_warn(hw,
18148c2ecf20Sopenharmony_ci			  "More SGEs than can be supported."
18158c2ecf20Sopenharmony_ci			  " SGEs: %d, Max SGEs: %d\n", nsge, scsim->max_sge);
18168c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scsim, n_unsupp_sge_error);
18178c2ecf20Sopenharmony_ci		goto err_dma_unmap;
18188c2ecf20Sopenharmony_ci	}
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci	/* Get a free ioreq structure - SM is already set to uninit */
18218c2ecf20Sopenharmony_ci	ioreq = csio_get_scsi_ioreq_lock(hw, scsim);
18228c2ecf20Sopenharmony_ci	if (!ioreq) {
18238c2ecf20Sopenharmony_ci		csio_err(hw, "Out of I/O request elements. Active #:%d\n",
18248c2ecf20Sopenharmony_ci			 scsim->stats.n_active);
18258c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scsim, n_no_req_error);
18268c2ecf20Sopenharmony_ci		goto err_dma_unmap;
18278c2ecf20Sopenharmony_ci	}
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	ioreq->nsge		= nsge;
18308c2ecf20Sopenharmony_ci	ioreq->lnode		= ln;
18318c2ecf20Sopenharmony_ci	ioreq->rnode		= rn;
18328c2ecf20Sopenharmony_ci	ioreq->iq_idx		= sqset->iq_idx;
18338c2ecf20Sopenharmony_ci	ioreq->eq_idx		= sqset->eq_idx;
18348c2ecf20Sopenharmony_ci	ioreq->wr_status	= 0;
18358c2ecf20Sopenharmony_ci	ioreq->drv_status	= 0;
18368c2ecf20Sopenharmony_ci	csio_scsi_cmnd(ioreq)	= (void *)cmnd;
18378c2ecf20Sopenharmony_ci	ioreq->tmo		= 0;
18388c2ecf20Sopenharmony_ci	ioreq->datadir		= cmnd->sc_data_direction;
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	if (cmnd->sc_data_direction == DMA_TO_DEVICE) {
18418c2ecf20Sopenharmony_ci		CSIO_INC_STATS(ln, n_output_requests);
18428c2ecf20Sopenharmony_ci		ln->stats.n_output_bytes += scsi_bufflen(cmnd);
18438c2ecf20Sopenharmony_ci	} else if (cmnd->sc_data_direction == DMA_FROM_DEVICE) {
18448c2ecf20Sopenharmony_ci		CSIO_INC_STATS(ln, n_input_requests);
18458c2ecf20Sopenharmony_ci		ln->stats.n_input_bytes += scsi_bufflen(cmnd);
18468c2ecf20Sopenharmony_ci	} else
18478c2ecf20Sopenharmony_ci		CSIO_INC_STATS(ln, n_control_requests);
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci	/* Set cbfn */
18508c2ecf20Sopenharmony_ci	ioreq->io_cbfn = csio_scsi_cbfn;
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	/* Needed during abort */
18538c2ecf20Sopenharmony_ci	cmnd->host_scribble = (unsigned char *)ioreq;
18548c2ecf20Sopenharmony_ci	cmnd->SCp.Message = 0;
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	/* Kick off SCSI IO SM on the ioreq */
18578c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hw->lock, flags);
18588c2ecf20Sopenharmony_ci	retval = csio_scsi_start_io(ioreq);
18598c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hw->lock, flags);
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci	if (retval != 0) {
18628c2ecf20Sopenharmony_ci		csio_err(hw, "ioreq: %p couldn't be started, status:%d\n",
18638c2ecf20Sopenharmony_ci			 ioreq, retval);
18648c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scsim, n_busy_error);
18658c2ecf20Sopenharmony_ci		goto err_put_req;
18668c2ecf20Sopenharmony_ci	}
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	return 0;
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_cierr_put_req:
18718c2ecf20Sopenharmony_ci	csio_put_scsi_ioreq_lock(hw, scsim, ioreq);
18728c2ecf20Sopenharmony_cierr_dma_unmap:
18738c2ecf20Sopenharmony_ci	if (nsge > 0)
18748c2ecf20Sopenharmony_ci		scsi_dma_unmap(cmnd);
18758c2ecf20Sopenharmony_cierr:
18768c2ecf20Sopenharmony_ci	return rv;
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_cierr_done:
18798c2ecf20Sopenharmony_ci	cmnd->scsi_done(cmnd);
18808c2ecf20Sopenharmony_ci	return 0;
18818c2ecf20Sopenharmony_ci}
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_cistatic int
18848c2ecf20Sopenharmony_cicsio_do_abrt_cls(struct csio_hw *hw, struct csio_ioreq *ioreq, bool abort)
18858c2ecf20Sopenharmony_ci{
18868c2ecf20Sopenharmony_ci	int rv;
18878c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
18888c2ecf20Sopenharmony_ci	struct csio_lnode *ln = ioreq->lnode;
18898c2ecf20Sopenharmony_ci	struct csio_scsi_qset *sqset = &hw->sqset[ln->portid][cpu];
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	ioreq->tmo = CSIO_SCSI_ABRT_TMO_MS;
18928c2ecf20Sopenharmony_ci	/*
18938c2ecf20Sopenharmony_ci	 * Use current processor queue for posting the abort/close, but retain
18948c2ecf20Sopenharmony_ci	 * the ingress queue ID of the original I/O being aborted/closed - we
18958c2ecf20Sopenharmony_ci	 * need the abort/close completion to be received on the same queue
18968c2ecf20Sopenharmony_ci	 * as the original I/O.
18978c2ecf20Sopenharmony_ci	 */
18988c2ecf20Sopenharmony_ci	ioreq->eq_idx = sqset->eq_idx;
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	if (abort == SCSI_ABORT)
19018c2ecf20Sopenharmony_ci		rv = csio_scsi_abort(ioreq);
19028c2ecf20Sopenharmony_ci	else
19038c2ecf20Sopenharmony_ci		rv = csio_scsi_close(ioreq);
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci	return rv;
19068c2ecf20Sopenharmony_ci}
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_cistatic int
19098c2ecf20Sopenharmony_cicsio_eh_abort_handler(struct scsi_cmnd *cmnd)
19108c2ecf20Sopenharmony_ci{
19118c2ecf20Sopenharmony_ci	struct csio_ioreq *ioreq;
19128c2ecf20Sopenharmony_ci	struct csio_lnode *ln = shost_priv(cmnd->device->host);
19138c2ecf20Sopenharmony_ci	struct csio_hw *hw = csio_lnode_to_hw(ln);
19148c2ecf20Sopenharmony_ci	struct csio_scsim *scsim = csio_hw_to_scsim(hw);
19158c2ecf20Sopenharmony_ci	int ready = 0, ret;
19168c2ecf20Sopenharmony_ci	unsigned long tmo = 0;
19178c2ecf20Sopenharmony_ci	int rv;
19188c2ecf20Sopenharmony_ci	struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata);
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	ret = fc_block_scsi_eh(cmnd);
19218c2ecf20Sopenharmony_ci	if (ret)
19228c2ecf20Sopenharmony_ci		return ret;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	ioreq = (struct csio_ioreq *)cmnd->host_scribble;
19258c2ecf20Sopenharmony_ci	if (!ioreq)
19268c2ecf20Sopenharmony_ci		return SUCCESS;
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_ci	if (!rn)
19298c2ecf20Sopenharmony_ci		return FAILED;
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci	csio_dbg(hw,
19328c2ecf20Sopenharmony_ci		 "Request to abort ioreq:%p cmd:%p cdb:%08llx"
19338c2ecf20Sopenharmony_ci		 " ssni:0x%x lun:%llu iq:0x%x\n",
19348c2ecf20Sopenharmony_ci		ioreq, cmnd, *((uint64_t *)cmnd->cmnd), rn->flowid,
19358c2ecf20Sopenharmony_ci		cmnd->device->lun, csio_q_physiqid(hw, ioreq->iq_idx));
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_ci	if (((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) != cmnd) {
19388c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scsim, n_abrt_race_comp);
19398c2ecf20Sopenharmony_ci		return SUCCESS;
19408c2ecf20Sopenharmony_ci	}
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci	ready = csio_is_lnode_ready(ln);
19438c2ecf20Sopenharmony_ci	tmo = CSIO_SCSI_ABRT_TMO_MS;
19448c2ecf20Sopenharmony_ci
19458c2ecf20Sopenharmony_ci	reinit_completion(&ioreq->cmplobj);
19468c2ecf20Sopenharmony_ci	spin_lock_irq(&hw->lock);
19478c2ecf20Sopenharmony_ci	rv = csio_do_abrt_cls(hw, ioreq, (ready ? SCSI_ABORT : SCSI_CLOSE));
19488c2ecf20Sopenharmony_ci	spin_unlock_irq(&hw->lock);
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_ci	if (rv != 0) {
19518c2ecf20Sopenharmony_ci		if (rv == -EINVAL) {
19528c2ecf20Sopenharmony_ci			/* Return success, if abort/close request issued on
19538c2ecf20Sopenharmony_ci			 * already completed IO
19548c2ecf20Sopenharmony_ci			 */
19558c2ecf20Sopenharmony_ci			return SUCCESS;
19568c2ecf20Sopenharmony_ci		}
19578c2ecf20Sopenharmony_ci		if (ready)
19588c2ecf20Sopenharmony_ci			CSIO_INC_STATS(scsim, n_abrt_busy_error);
19598c2ecf20Sopenharmony_ci		else
19608c2ecf20Sopenharmony_ci			CSIO_INC_STATS(scsim, n_cls_busy_error);
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci		goto inval_scmnd;
19638c2ecf20Sopenharmony_ci	}
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci	wait_for_completion_timeout(&ioreq->cmplobj, msecs_to_jiffies(tmo));
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_ci	/* FW didnt respond to abort within our timeout */
19688c2ecf20Sopenharmony_ci	if (((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) == cmnd) {
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci		csio_err(hw, "Abort timed out -- req: %p\n", ioreq);
19718c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scsim, n_abrt_timedout);
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ciinval_scmnd:
19748c2ecf20Sopenharmony_ci		if (ioreq->nsge > 0)
19758c2ecf20Sopenharmony_ci			scsi_dma_unmap(cmnd);
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci		spin_lock_irq(&hw->lock);
19788c2ecf20Sopenharmony_ci		csio_scsi_cmnd(ioreq) = NULL;
19798c2ecf20Sopenharmony_ci		spin_unlock_irq(&hw->lock);
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci		cmnd->result = (DID_ERROR << 16);
19828c2ecf20Sopenharmony_ci		cmnd->scsi_done(cmnd);
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci		return FAILED;
19858c2ecf20Sopenharmony_ci	}
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	/* FW successfully aborted the request */
19888c2ecf20Sopenharmony_ci	if (host_byte(cmnd->result) == DID_REQUEUE) {
19898c2ecf20Sopenharmony_ci		csio_info(hw,
19908c2ecf20Sopenharmony_ci			"Aborted SCSI command to (%d:%llu) tag %u\n",
19918c2ecf20Sopenharmony_ci			cmnd->device->id, cmnd->device->lun,
19928c2ecf20Sopenharmony_ci			cmnd->request->tag);
19938c2ecf20Sopenharmony_ci		return SUCCESS;
19948c2ecf20Sopenharmony_ci	} else {
19958c2ecf20Sopenharmony_ci		csio_info(hw,
19968c2ecf20Sopenharmony_ci			"Failed to abort SCSI command, (%d:%llu) tag %u\n",
19978c2ecf20Sopenharmony_ci			cmnd->device->id, cmnd->device->lun,
19988c2ecf20Sopenharmony_ci			cmnd->request->tag);
19998c2ecf20Sopenharmony_ci		return FAILED;
20008c2ecf20Sopenharmony_ci	}
20018c2ecf20Sopenharmony_ci}
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci/*
20048c2ecf20Sopenharmony_ci * csio_tm_cbfn - TM callback function.
20058c2ecf20Sopenharmony_ci * @hw: HW module.
20068c2ecf20Sopenharmony_ci * @req: IO request.
20078c2ecf20Sopenharmony_ci *
20088c2ecf20Sopenharmony_ci * Cache the result in 'cmnd', since ioreq will be freed soon
20098c2ecf20Sopenharmony_ci * after we return from here, and the waiting thread shouldnt trust
20108c2ecf20Sopenharmony_ci * the ioreq contents.
20118c2ecf20Sopenharmony_ci */
20128c2ecf20Sopenharmony_cistatic void
20138c2ecf20Sopenharmony_cicsio_tm_cbfn(struct csio_hw *hw, struct csio_ioreq *req)
20148c2ecf20Sopenharmony_ci{
20158c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmnd  = (struct scsi_cmnd *)csio_scsi_cmnd(req);
20168c2ecf20Sopenharmony_ci	struct csio_dma_buf *dma_buf;
20178c2ecf20Sopenharmony_ci	uint8_t flags = 0;
20188c2ecf20Sopenharmony_ci	struct fcp_resp_with_ext *fcp_resp;
20198c2ecf20Sopenharmony_ci	struct fcp_resp_rsp_info *rsp_info;
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	csio_dbg(hw, "req: %p in csio_tm_cbfn status: %d\n",
20228c2ecf20Sopenharmony_ci		      req, req->wr_status);
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	/* Cache FW return status */
20258c2ecf20Sopenharmony_ci	cmnd->SCp.Status = req->wr_status;
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	/* Special handling based on FCP response */
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci	/*
20308c2ecf20Sopenharmony_ci	 * FW returns us this error, if flags were set. FCP4 says
20318c2ecf20Sopenharmony_ci	 * FCP_RSP_LEN_VAL in flags shall be set for TM completions.
20328c2ecf20Sopenharmony_ci	 * So if a target were to set this bit, we expect that the
20338c2ecf20Sopenharmony_ci	 * rsp_code is set to FCP_TMF_CMPL for a successful TM
20348c2ecf20Sopenharmony_ci	 * completion. Any other rsp_code means TM operation failed.
20358c2ecf20Sopenharmony_ci	 * If a target were to just ignore setting flags, we treat
20368c2ecf20Sopenharmony_ci	 * the TM operation as success, and FW returns FW_SUCCESS.
20378c2ecf20Sopenharmony_ci	 */
20388c2ecf20Sopenharmony_ci	if (req->wr_status == FW_SCSI_RSP_ERR) {
20398c2ecf20Sopenharmony_ci		dma_buf = &req->dma_buf;
20408c2ecf20Sopenharmony_ci		fcp_resp = (struct fcp_resp_with_ext *)dma_buf->vaddr;
20418c2ecf20Sopenharmony_ci		rsp_info = (struct fcp_resp_rsp_info *)(fcp_resp + 1);
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci		flags = fcp_resp->resp.fr_flags;
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci		/* Modify return status if flags indicate success */
20468c2ecf20Sopenharmony_ci		if (flags & FCP_RSP_LEN_VAL)
20478c2ecf20Sopenharmony_ci			if (rsp_info->rsp_code == FCP_TMF_CMPL)
20488c2ecf20Sopenharmony_ci				cmnd->SCp.Status = FW_SUCCESS;
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci		csio_dbg(hw, "TM FCP rsp code: %d\n", rsp_info->rsp_code);
20518c2ecf20Sopenharmony_ci	}
20528c2ecf20Sopenharmony_ci
20538c2ecf20Sopenharmony_ci	/* Wake up the TM handler thread */
20548c2ecf20Sopenharmony_ci	csio_scsi_cmnd(req) = NULL;
20558c2ecf20Sopenharmony_ci}
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_cistatic int
20588c2ecf20Sopenharmony_cicsio_eh_lun_reset_handler(struct scsi_cmnd *cmnd)
20598c2ecf20Sopenharmony_ci{
20608c2ecf20Sopenharmony_ci	struct csio_lnode *ln = shost_priv(cmnd->device->host);
20618c2ecf20Sopenharmony_ci	struct csio_hw *hw = csio_lnode_to_hw(ln);
20628c2ecf20Sopenharmony_ci	struct csio_scsim *scsim = csio_hw_to_scsim(hw);
20638c2ecf20Sopenharmony_ci	struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata);
20648c2ecf20Sopenharmony_ci	struct csio_ioreq *ioreq = NULL;
20658c2ecf20Sopenharmony_ci	struct csio_scsi_qset *sqset;
20668c2ecf20Sopenharmony_ci	unsigned long flags;
20678c2ecf20Sopenharmony_ci	int retval;
20688c2ecf20Sopenharmony_ci	int count, ret;
20698c2ecf20Sopenharmony_ci	LIST_HEAD(local_q);
20708c2ecf20Sopenharmony_ci	struct csio_scsi_level_data sld;
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	if (!rn)
20738c2ecf20Sopenharmony_ci		goto fail;
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci	csio_dbg(hw, "Request to reset LUN:%llu (ssni:0x%x tgtid:%d)\n",
20768c2ecf20Sopenharmony_ci		      cmnd->device->lun, rn->flowid, rn->scsi_id);
20778c2ecf20Sopenharmony_ci
20788c2ecf20Sopenharmony_ci	if (!csio_is_lnode_ready(ln)) {
20798c2ecf20Sopenharmony_ci		csio_err(hw,
20808c2ecf20Sopenharmony_ci			 "LUN reset cannot be issued on non-ready"
20818c2ecf20Sopenharmony_ci			 " local node vnpi:0x%x (LUN:%llu)\n",
20828c2ecf20Sopenharmony_ci			 ln->vnp_flowid, cmnd->device->lun);
20838c2ecf20Sopenharmony_ci		goto fail;
20848c2ecf20Sopenharmony_ci	}
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci	/* Lnode is ready, now wait on rport node readiness */
20878c2ecf20Sopenharmony_ci	ret = fc_block_scsi_eh(cmnd);
20888c2ecf20Sopenharmony_ci	if (ret)
20898c2ecf20Sopenharmony_ci		return ret;
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	/*
20928c2ecf20Sopenharmony_ci	 * If we have blocked in the previous call, at this point, either the
20938c2ecf20Sopenharmony_ci	 * remote node has come back online, or device loss timer has fired
20948c2ecf20Sopenharmony_ci	 * and the remote node is destroyed. Allow the LUN reset only for
20958c2ecf20Sopenharmony_ci	 * the former case, since LUN reset is a TMF I/O on the wire, and we
20968c2ecf20Sopenharmony_ci	 * need a valid session to issue it.
20978c2ecf20Sopenharmony_ci	 */
20988c2ecf20Sopenharmony_ci	if (fc_remote_port_chkready(rn->rport)) {
20998c2ecf20Sopenharmony_ci		csio_err(hw,
21008c2ecf20Sopenharmony_ci			 "LUN reset cannot be issued on non-ready"
21018c2ecf20Sopenharmony_ci			 " remote node ssni:0x%x (LUN:%llu)\n",
21028c2ecf20Sopenharmony_ci			 rn->flowid, cmnd->device->lun);
21038c2ecf20Sopenharmony_ci		goto fail;
21048c2ecf20Sopenharmony_ci	}
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	/* Get a free ioreq structure - SM is already set to uninit */
21078c2ecf20Sopenharmony_ci	ioreq = csio_get_scsi_ioreq_lock(hw, scsim);
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci	if (!ioreq) {
21108c2ecf20Sopenharmony_ci		csio_err(hw, "Out of IO request elements. Active # :%d\n",
21118c2ecf20Sopenharmony_ci			 scsim->stats.n_active);
21128c2ecf20Sopenharmony_ci		goto fail;
21138c2ecf20Sopenharmony_ci	}
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci	sqset			= &hw->sqset[ln->portid][smp_processor_id()];
21168c2ecf20Sopenharmony_ci	ioreq->nsge		= 0;
21178c2ecf20Sopenharmony_ci	ioreq->lnode		= ln;
21188c2ecf20Sopenharmony_ci	ioreq->rnode		= rn;
21198c2ecf20Sopenharmony_ci	ioreq->iq_idx		= sqset->iq_idx;
21208c2ecf20Sopenharmony_ci	ioreq->eq_idx		= sqset->eq_idx;
21218c2ecf20Sopenharmony_ci
21228c2ecf20Sopenharmony_ci	csio_scsi_cmnd(ioreq)	= cmnd;
21238c2ecf20Sopenharmony_ci	cmnd->host_scribble	= (unsigned char *)ioreq;
21248c2ecf20Sopenharmony_ci	cmnd->SCp.Status	= 0;
21258c2ecf20Sopenharmony_ci
21268c2ecf20Sopenharmony_ci	cmnd->SCp.Message	= FCP_TMF_LUN_RESET;
21278c2ecf20Sopenharmony_ci	ioreq->tmo		= CSIO_SCSI_LUNRST_TMO_MS / 1000;
21288c2ecf20Sopenharmony_ci
21298c2ecf20Sopenharmony_ci	/*
21308c2ecf20Sopenharmony_ci	 * FW times the LUN reset for ioreq->tmo, so we got to wait a little
21318c2ecf20Sopenharmony_ci	 * longer (10s for now) than that to allow FW to return the timed
21328c2ecf20Sopenharmony_ci	 * out command.
21338c2ecf20Sopenharmony_ci	 */
21348c2ecf20Sopenharmony_ci	count = DIV_ROUND_UP((ioreq->tmo + 10) * 1000, CSIO_SCSI_TM_POLL_MS);
21358c2ecf20Sopenharmony_ci
21368c2ecf20Sopenharmony_ci	/* Set cbfn */
21378c2ecf20Sopenharmony_ci	ioreq->io_cbfn = csio_tm_cbfn;
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci	/* Save of the ioreq info for later use */
21408c2ecf20Sopenharmony_ci	sld.level = CSIO_LEV_LUN;
21418c2ecf20Sopenharmony_ci	sld.lnode = ioreq->lnode;
21428c2ecf20Sopenharmony_ci	sld.rnode = ioreq->rnode;
21438c2ecf20Sopenharmony_ci	sld.oslun = cmnd->device->lun;
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hw->lock, flags);
21468c2ecf20Sopenharmony_ci	/* Kick off TM SM on the ioreq */
21478c2ecf20Sopenharmony_ci	retval = csio_scsi_start_tm(ioreq);
21488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hw->lock, flags);
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_ci	if (retval != 0) {
21518c2ecf20Sopenharmony_ci		csio_err(hw, "Failed to issue LUN reset, req:%p, status:%d\n",
21528c2ecf20Sopenharmony_ci			    ioreq, retval);
21538c2ecf20Sopenharmony_ci		goto fail_ret_ioreq;
21548c2ecf20Sopenharmony_ci	}
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci	csio_dbg(hw, "Waiting max %d secs for LUN reset completion\n",
21578c2ecf20Sopenharmony_ci		    count * (CSIO_SCSI_TM_POLL_MS / 1000));
21588c2ecf20Sopenharmony_ci	/* Wait for completion */
21598c2ecf20Sopenharmony_ci	while ((((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) == cmnd)
21608c2ecf20Sopenharmony_ci								&& count--)
21618c2ecf20Sopenharmony_ci		msleep(CSIO_SCSI_TM_POLL_MS);
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	/* LUN reset timed-out */
21648c2ecf20Sopenharmony_ci	if (((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) == cmnd) {
21658c2ecf20Sopenharmony_ci		csio_err(hw, "LUN reset (%d:%llu) timed out\n",
21668c2ecf20Sopenharmony_ci			 cmnd->device->id, cmnd->device->lun);
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_ci		spin_lock_irq(&hw->lock);
21698c2ecf20Sopenharmony_ci		csio_scsi_drvcleanup(ioreq);
21708c2ecf20Sopenharmony_ci		list_del_init(&ioreq->sm.sm_list);
21718c2ecf20Sopenharmony_ci		spin_unlock_irq(&hw->lock);
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci		goto fail_ret_ioreq;
21748c2ecf20Sopenharmony_ci	}
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_ci	/* LUN reset returned, check cached status */
21778c2ecf20Sopenharmony_ci	if (cmnd->SCp.Status != FW_SUCCESS) {
21788c2ecf20Sopenharmony_ci		csio_err(hw, "LUN reset failed (%d:%llu), status: %d\n",
21798c2ecf20Sopenharmony_ci			 cmnd->device->id, cmnd->device->lun, cmnd->SCp.Status);
21808c2ecf20Sopenharmony_ci		goto fail;
21818c2ecf20Sopenharmony_ci	}
21828c2ecf20Sopenharmony_ci
21838c2ecf20Sopenharmony_ci	/* LUN reset succeeded, Start aborting affected I/Os */
21848c2ecf20Sopenharmony_ci	/*
21858c2ecf20Sopenharmony_ci	 * Since the host guarantees during LUN reset that there
21868c2ecf20Sopenharmony_ci	 * will not be any more I/Os to that LUN, until the LUN reset
21878c2ecf20Sopenharmony_ci	 * completes, we gather pending I/Os after the LUN reset.
21888c2ecf20Sopenharmony_ci	 */
21898c2ecf20Sopenharmony_ci	spin_lock_irq(&hw->lock);
21908c2ecf20Sopenharmony_ci	csio_scsi_gather_active_ios(scsim, &sld, &local_q);
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci	retval = csio_scsi_abort_io_q(scsim, &local_q, 30000);
21938c2ecf20Sopenharmony_ci	spin_unlock_irq(&hw->lock);
21948c2ecf20Sopenharmony_ci
21958c2ecf20Sopenharmony_ci	/* Aborts may have timed out */
21968c2ecf20Sopenharmony_ci	if (retval != 0) {
21978c2ecf20Sopenharmony_ci		csio_err(hw,
21988c2ecf20Sopenharmony_ci			 "Attempt to abort I/Os during LUN reset of %llu"
21998c2ecf20Sopenharmony_ci			 " returned %d\n", cmnd->device->lun, retval);
22008c2ecf20Sopenharmony_ci		/* Return I/Os back to active_q */
22018c2ecf20Sopenharmony_ci		spin_lock_irq(&hw->lock);
22028c2ecf20Sopenharmony_ci		list_splice_tail_init(&local_q, &scsim->active_q);
22038c2ecf20Sopenharmony_ci		spin_unlock_irq(&hw->lock);
22048c2ecf20Sopenharmony_ci		goto fail;
22058c2ecf20Sopenharmony_ci	}
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci	CSIO_INC_STATS(rn, n_lun_rst);
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	csio_info(hw, "LUN reset occurred (%d:%llu)\n",
22108c2ecf20Sopenharmony_ci		  cmnd->device->id, cmnd->device->lun);
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	return SUCCESS;
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_cifail_ret_ioreq:
22158c2ecf20Sopenharmony_ci	csio_put_scsi_ioreq_lock(hw, scsim, ioreq);
22168c2ecf20Sopenharmony_cifail:
22178c2ecf20Sopenharmony_ci	CSIO_INC_STATS(rn, n_lun_rst_fail);
22188c2ecf20Sopenharmony_ci	return FAILED;
22198c2ecf20Sopenharmony_ci}
22208c2ecf20Sopenharmony_ci
22218c2ecf20Sopenharmony_cistatic int
22228c2ecf20Sopenharmony_cicsio_slave_alloc(struct scsi_device *sdev)
22238c2ecf20Sopenharmony_ci{
22248c2ecf20Sopenharmony_ci	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci	if (!rport || fc_remote_port_chkready(rport))
22278c2ecf20Sopenharmony_ci		return -ENXIO;
22288c2ecf20Sopenharmony_ci
22298c2ecf20Sopenharmony_ci	sdev->hostdata = *((struct csio_lnode **)(rport->dd_data));
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	return 0;
22328c2ecf20Sopenharmony_ci}
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_cistatic int
22358c2ecf20Sopenharmony_cicsio_slave_configure(struct scsi_device *sdev)
22368c2ecf20Sopenharmony_ci{
22378c2ecf20Sopenharmony_ci	scsi_change_queue_depth(sdev, csio_lun_qdepth);
22388c2ecf20Sopenharmony_ci	return 0;
22398c2ecf20Sopenharmony_ci}
22408c2ecf20Sopenharmony_ci
22418c2ecf20Sopenharmony_cistatic void
22428c2ecf20Sopenharmony_cicsio_slave_destroy(struct scsi_device *sdev)
22438c2ecf20Sopenharmony_ci{
22448c2ecf20Sopenharmony_ci	sdev->hostdata = NULL;
22458c2ecf20Sopenharmony_ci}
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_cistatic int
22488c2ecf20Sopenharmony_cicsio_scan_finished(struct Scsi_Host *shost, unsigned long time)
22498c2ecf20Sopenharmony_ci{
22508c2ecf20Sopenharmony_ci	struct csio_lnode *ln = shost_priv(shost);
22518c2ecf20Sopenharmony_ci	int rv = 1;
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci	spin_lock_irq(shost->host_lock);
22548c2ecf20Sopenharmony_ci	if (!ln->hwp || csio_list_deleted(&ln->sm.sm_list))
22558c2ecf20Sopenharmony_ci		goto out;
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_ci	rv = csio_scan_done(ln, jiffies, time, csio_max_scan_tmo * HZ,
22588c2ecf20Sopenharmony_ci			    csio_delta_scan_tmo * HZ);
22598c2ecf20Sopenharmony_ciout:
22608c2ecf20Sopenharmony_ci	spin_unlock_irq(shost->host_lock);
22618c2ecf20Sopenharmony_ci
22628c2ecf20Sopenharmony_ci	return rv;
22638c2ecf20Sopenharmony_ci}
22648c2ecf20Sopenharmony_ci
22658c2ecf20Sopenharmony_cistruct scsi_host_template csio_fcoe_shost_template = {
22668c2ecf20Sopenharmony_ci	.module			= THIS_MODULE,
22678c2ecf20Sopenharmony_ci	.name			= CSIO_DRV_DESC,
22688c2ecf20Sopenharmony_ci	.proc_name		= KBUILD_MODNAME,
22698c2ecf20Sopenharmony_ci	.queuecommand		= csio_queuecommand,
22708c2ecf20Sopenharmony_ci	.eh_timed_out		= fc_eh_timed_out,
22718c2ecf20Sopenharmony_ci	.eh_abort_handler	= csio_eh_abort_handler,
22728c2ecf20Sopenharmony_ci	.eh_device_reset_handler = csio_eh_lun_reset_handler,
22738c2ecf20Sopenharmony_ci	.slave_alloc		= csio_slave_alloc,
22748c2ecf20Sopenharmony_ci	.slave_configure	= csio_slave_configure,
22758c2ecf20Sopenharmony_ci	.slave_destroy		= csio_slave_destroy,
22768c2ecf20Sopenharmony_ci	.scan_finished		= csio_scan_finished,
22778c2ecf20Sopenharmony_ci	.this_id		= -1,
22788c2ecf20Sopenharmony_ci	.sg_tablesize		= CSIO_SCSI_MAX_SGE,
22798c2ecf20Sopenharmony_ci	.cmd_per_lun		= CSIO_MAX_CMD_PER_LUN,
22808c2ecf20Sopenharmony_ci	.shost_attrs		= csio_fcoe_lport_attrs,
22818c2ecf20Sopenharmony_ci	.max_sectors		= CSIO_MAX_SECTOR_SIZE,
22828c2ecf20Sopenharmony_ci};
22838c2ecf20Sopenharmony_ci
22848c2ecf20Sopenharmony_cistruct scsi_host_template csio_fcoe_shost_vport_template = {
22858c2ecf20Sopenharmony_ci	.module			= THIS_MODULE,
22868c2ecf20Sopenharmony_ci	.name			= CSIO_DRV_DESC,
22878c2ecf20Sopenharmony_ci	.proc_name		= KBUILD_MODNAME,
22888c2ecf20Sopenharmony_ci	.queuecommand		= csio_queuecommand,
22898c2ecf20Sopenharmony_ci	.eh_timed_out		= fc_eh_timed_out,
22908c2ecf20Sopenharmony_ci	.eh_abort_handler	= csio_eh_abort_handler,
22918c2ecf20Sopenharmony_ci	.eh_device_reset_handler = csio_eh_lun_reset_handler,
22928c2ecf20Sopenharmony_ci	.slave_alloc		= csio_slave_alloc,
22938c2ecf20Sopenharmony_ci	.slave_configure	= csio_slave_configure,
22948c2ecf20Sopenharmony_ci	.slave_destroy		= csio_slave_destroy,
22958c2ecf20Sopenharmony_ci	.scan_finished		= csio_scan_finished,
22968c2ecf20Sopenharmony_ci	.this_id		= -1,
22978c2ecf20Sopenharmony_ci	.sg_tablesize		= CSIO_SCSI_MAX_SGE,
22988c2ecf20Sopenharmony_ci	.cmd_per_lun		= CSIO_MAX_CMD_PER_LUN,
22998c2ecf20Sopenharmony_ci	.shost_attrs		= csio_fcoe_vport_attrs,
23008c2ecf20Sopenharmony_ci	.max_sectors		= CSIO_MAX_SECTOR_SIZE,
23018c2ecf20Sopenharmony_ci};
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ci/*
23048c2ecf20Sopenharmony_ci * csio_scsi_alloc_ddp_bufs - Allocate buffers for DDP of unaligned SGLs.
23058c2ecf20Sopenharmony_ci * @scm: SCSI Module
23068c2ecf20Sopenharmony_ci * @hw: HW device.
23078c2ecf20Sopenharmony_ci * @buf_size: buffer size
23088c2ecf20Sopenharmony_ci * @num_buf : Number of buffers.
23098c2ecf20Sopenharmony_ci *
23108c2ecf20Sopenharmony_ci * This routine allocates DMA buffers required for SCSI Data xfer, if
23118c2ecf20Sopenharmony_ci * each SGL buffer for a SCSI Read request posted by SCSI midlayer are
23128c2ecf20Sopenharmony_ci * not virtually contiguous.
23138c2ecf20Sopenharmony_ci */
23148c2ecf20Sopenharmony_cistatic int
23158c2ecf20Sopenharmony_cicsio_scsi_alloc_ddp_bufs(struct csio_scsim *scm, struct csio_hw *hw,
23168c2ecf20Sopenharmony_ci			 int buf_size, int num_buf)
23178c2ecf20Sopenharmony_ci{
23188c2ecf20Sopenharmony_ci	int n = 0;
23198c2ecf20Sopenharmony_ci	struct list_head *tmp;
23208c2ecf20Sopenharmony_ci	struct csio_dma_buf *ddp_desc = NULL;
23218c2ecf20Sopenharmony_ci	uint32_t unit_size = 0;
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci	if (!num_buf)
23248c2ecf20Sopenharmony_ci		return 0;
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_ci	if (!buf_size)
23278c2ecf20Sopenharmony_ci		return -EINVAL;
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&scm->ddp_freelist);
23308c2ecf20Sopenharmony_ci
23318c2ecf20Sopenharmony_ci	/* Align buf size to page size */
23328c2ecf20Sopenharmony_ci	buf_size = (buf_size + PAGE_SIZE - 1) & PAGE_MASK;
23338c2ecf20Sopenharmony_ci	/* Initialize dma descriptors */
23348c2ecf20Sopenharmony_ci	for (n = 0; n < num_buf; n++) {
23358c2ecf20Sopenharmony_ci		/* Set unit size to request size */
23368c2ecf20Sopenharmony_ci		unit_size = buf_size;
23378c2ecf20Sopenharmony_ci		ddp_desc = kzalloc(sizeof(struct csio_dma_buf), GFP_KERNEL);
23388c2ecf20Sopenharmony_ci		if (!ddp_desc) {
23398c2ecf20Sopenharmony_ci			csio_err(hw,
23408c2ecf20Sopenharmony_ci				 "Failed to allocate ddp descriptors,"
23418c2ecf20Sopenharmony_ci				 " Num allocated = %d.\n",
23428c2ecf20Sopenharmony_ci				 scm->stats.n_free_ddp);
23438c2ecf20Sopenharmony_ci			goto no_mem;
23448c2ecf20Sopenharmony_ci		}
23458c2ecf20Sopenharmony_ci
23468c2ecf20Sopenharmony_ci		/* Allocate Dma buffers for DDP */
23478c2ecf20Sopenharmony_ci		ddp_desc->vaddr = dma_alloc_coherent(&hw->pdev->dev, unit_size,
23488c2ecf20Sopenharmony_ci				&ddp_desc->paddr, GFP_KERNEL);
23498c2ecf20Sopenharmony_ci		if (!ddp_desc->vaddr) {
23508c2ecf20Sopenharmony_ci			csio_err(hw,
23518c2ecf20Sopenharmony_ci				 "SCSI response DMA buffer (ddp) allocation"
23528c2ecf20Sopenharmony_ci				 " failed!\n");
23538c2ecf20Sopenharmony_ci			kfree(ddp_desc);
23548c2ecf20Sopenharmony_ci			goto no_mem;
23558c2ecf20Sopenharmony_ci		}
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_ci		ddp_desc->len = unit_size;
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci		/* Added it to scsi ddp freelist */
23608c2ecf20Sopenharmony_ci		list_add_tail(&ddp_desc->list, &scm->ddp_freelist);
23618c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_free_ddp);
23628c2ecf20Sopenharmony_ci	}
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci	return 0;
23658c2ecf20Sopenharmony_cino_mem:
23668c2ecf20Sopenharmony_ci	/* release dma descs back to freelist and free dma memory */
23678c2ecf20Sopenharmony_ci	list_for_each(tmp, &scm->ddp_freelist) {
23688c2ecf20Sopenharmony_ci		ddp_desc = (struct csio_dma_buf *) tmp;
23698c2ecf20Sopenharmony_ci		tmp = csio_list_prev(tmp);
23708c2ecf20Sopenharmony_ci		dma_free_coherent(&hw->pdev->dev, ddp_desc->len,
23718c2ecf20Sopenharmony_ci				  ddp_desc->vaddr, ddp_desc->paddr);
23728c2ecf20Sopenharmony_ci		list_del_init(&ddp_desc->list);
23738c2ecf20Sopenharmony_ci		kfree(ddp_desc);
23748c2ecf20Sopenharmony_ci	}
23758c2ecf20Sopenharmony_ci	scm->stats.n_free_ddp = 0;
23768c2ecf20Sopenharmony_ci
23778c2ecf20Sopenharmony_ci	return -ENOMEM;
23788c2ecf20Sopenharmony_ci}
23798c2ecf20Sopenharmony_ci
23808c2ecf20Sopenharmony_ci/*
23818c2ecf20Sopenharmony_ci * csio_scsi_free_ddp_bufs - free DDP buffers of unaligned SGLs.
23828c2ecf20Sopenharmony_ci * @scm: SCSI Module
23838c2ecf20Sopenharmony_ci * @hw: HW device.
23848c2ecf20Sopenharmony_ci *
23858c2ecf20Sopenharmony_ci * This routine frees ddp buffers.
23868c2ecf20Sopenharmony_ci */
23878c2ecf20Sopenharmony_cistatic void
23888c2ecf20Sopenharmony_cicsio_scsi_free_ddp_bufs(struct csio_scsim *scm, struct csio_hw *hw)
23898c2ecf20Sopenharmony_ci{
23908c2ecf20Sopenharmony_ci	struct list_head *tmp;
23918c2ecf20Sopenharmony_ci	struct csio_dma_buf *ddp_desc;
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_ci	/* release dma descs back to freelist and free dma memory */
23948c2ecf20Sopenharmony_ci	list_for_each(tmp, &scm->ddp_freelist) {
23958c2ecf20Sopenharmony_ci		ddp_desc = (struct csio_dma_buf *) tmp;
23968c2ecf20Sopenharmony_ci		tmp = csio_list_prev(tmp);
23978c2ecf20Sopenharmony_ci		dma_free_coherent(&hw->pdev->dev, ddp_desc->len,
23988c2ecf20Sopenharmony_ci				  ddp_desc->vaddr, ddp_desc->paddr);
23998c2ecf20Sopenharmony_ci		list_del_init(&ddp_desc->list);
24008c2ecf20Sopenharmony_ci		kfree(ddp_desc);
24018c2ecf20Sopenharmony_ci	}
24028c2ecf20Sopenharmony_ci	scm->stats.n_free_ddp = 0;
24038c2ecf20Sopenharmony_ci}
24048c2ecf20Sopenharmony_ci
24058c2ecf20Sopenharmony_ci/**
24068c2ecf20Sopenharmony_ci * csio_scsim_init - Initialize SCSI Module
24078c2ecf20Sopenharmony_ci * @scm:	SCSI Module
24088c2ecf20Sopenharmony_ci * @hw:		HW module
24098c2ecf20Sopenharmony_ci *
24108c2ecf20Sopenharmony_ci */
24118c2ecf20Sopenharmony_ciint
24128c2ecf20Sopenharmony_cicsio_scsim_init(struct csio_scsim *scm, struct csio_hw *hw)
24138c2ecf20Sopenharmony_ci{
24148c2ecf20Sopenharmony_ci	int i;
24158c2ecf20Sopenharmony_ci	struct csio_ioreq *ioreq;
24168c2ecf20Sopenharmony_ci	struct csio_dma_buf *dma_buf;
24178c2ecf20Sopenharmony_ci
24188c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&scm->active_q);
24198c2ecf20Sopenharmony_ci	scm->hw = hw;
24208c2ecf20Sopenharmony_ci
24218c2ecf20Sopenharmony_ci	scm->proto_cmd_len = sizeof(struct fcp_cmnd);
24228c2ecf20Sopenharmony_ci	scm->proto_rsp_len = CSIO_SCSI_RSP_LEN;
24238c2ecf20Sopenharmony_ci	scm->max_sge = CSIO_SCSI_MAX_SGE;
24248c2ecf20Sopenharmony_ci
24258c2ecf20Sopenharmony_ci	spin_lock_init(&scm->freelist_lock);
24268c2ecf20Sopenharmony_ci
24278c2ecf20Sopenharmony_ci	/* Pre-allocate ioreqs and initialize them */
24288c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&scm->ioreq_freelist);
24298c2ecf20Sopenharmony_ci	for (i = 0; i < csio_scsi_ioreqs; i++) {
24308c2ecf20Sopenharmony_ci
24318c2ecf20Sopenharmony_ci		ioreq = kzalloc(sizeof(struct csio_ioreq), GFP_KERNEL);
24328c2ecf20Sopenharmony_ci		if (!ioreq) {
24338c2ecf20Sopenharmony_ci			csio_err(hw,
24348c2ecf20Sopenharmony_ci				 "I/O request element allocation failed, "
24358c2ecf20Sopenharmony_ci				 " Num allocated = %d.\n",
24368c2ecf20Sopenharmony_ci				 scm->stats.n_free_ioreq);
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_ci			goto free_ioreq;
24398c2ecf20Sopenharmony_ci		}
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_ci		/* Allocate Dma buffers for Response Payload */
24428c2ecf20Sopenharmony_ci		dma_buf = &ioreq->dma_buf;
24438c2ecf20Sopenharmony_ci		dma_buf->vaddr = dma_pool_alloc(hw->scsi_dma_pool, GFP_KERNEL,
24448c2ecf20Sopenharmony_ci						&dma_buf->paddr);
24458c2ecf20Sopenharmony_ci		if (!dma_buf->vaddr) {
24468c2ecf20Sopenharmony_ci			csio_err(hw,
24478c2ecf20Sopenharmony_ci				 "SCSI response DMA buffer allocation"
24488c2ecf20Sopenharmony_ci				 " failed!\n");
24498c2ecf20Sopenharmony_ci			kfree(ioreq);
24508c2ecf20Sopenharmony_ci			goto free_ioreq;
24518c2ecf20Sopenharmony_ci		}
24528c2ecf20Sopenharmony_ci
24538c2ecf20Sopenharmony_ci		dma_buf->len = scm->proto_rsp_len;
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci		/* Set state to uninit */
24568c2ecf20Sopenharmony_ci		csio_init_state(&ioreq->sm, csio_scsis_uninit);
24578c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&ioreq->gen_list);
24588c2ecf20Sopenharmony_ci		init_completion(&ioreq->cmplobj);
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci		list_add_tail(&ioreq->sm.sm_list, &scm->ioreq_freelist);
24618c2ecf20Sopenharmony_ci		CSIO_INC_STATS(scm, n_free_ioreq);
24628c2ecf20Sopenharmony_ci	}
24638c2ecf20Sopenharmony_ci
24648c2ecf20Sopenharmony_ci	if (csio_scsi_alloc_ddp_bufs(scm, hw, PAGE_SIZE, csio_ddp_descs))
24658c2ecf20Sopenharmony_ci		goto free_ioreq;
24668c2ecf20Sopenharmony_ci
24678c2ecf20Sopenharmony_ci	return 0;
24688c2ecf20Sopenharmony_ci
24698c2ecf20Sopenharmony_cifree_ioreq:
24708c2ecf20Sopenharmony_ci	/*
24718c2ecf20Sopenharmony_ci	 * Free up existing allocations, since an error
24728c2ecf20Sopenharmony_ci	 * from here means we are returning for good
24738c2ecf20Sopenharmony_ci	 */
24748c2ecf20Sopenharmony_ci	while (!list_empty(&scm->ioreq_freelist)) {
24758c2ecf20Sopenharmony_ci		struct csio_sm *tmp;
24768c2ecf20Sopenharmony_ci
24778c2ecf20Sopenharmony_ci		tmp = list_first_entry(&scm->ioreq_freelist,
24788c2ecf20Sopenharmony_ci				       struct csio_sm, sm_list);
24798c2ecf20Sopenharmony_ci		list_del_init(&tmp->sm_list);
24808c2ecf20Sopenharmony_ci		ioreq = (struct csio_ioreq *)tmp;
24818c2ecf20Sopenharmony_ci
24828c2ecf20Sopenharmony_ci		dma_buf = &ioreq->dma_buf;
24838c2ecf20Sopenharmony_ci		dma_pool_free(hw->scsi_dma_pool, dma_buf->vaddr,
24848c2ecf20Sopenharmony_ci			      dma_buf->paddr);
24858c2ecf20Sopenharmony_ci
24868c2ecf20Sopenharmony_ci		kfree(ioreq);
24878c2ecf20Sopenharmony_ci	}
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	scm->stats.n_free_ioreq = 0;
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_ci	return -ENOMEM;
24928c2ecf20Sopenharmony_ci}
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci/**
24958c2ecf20Sopenharmony_ci * csio_scsim_exit: Uninitialize SCSI Module
24968c2ecf20Sopenharmony_ci * @scm: SCSI Module
24978c2ecf20Sopenharmony_ci *
24988c2ecf20Sopenharmony_ci */
24998c2ecf20Sopenharmony_civoid
25008c2ecf20Sopenharmony_cicsio_scsim_exit(struct csio_scsim *scm)
25018c2ecf20Sopenharmony_ci{
25028c2ecf20Sopenharmony_ci	struct csio_ioreq *ioreq;
25038c2ecf20Sopenharmony_ci	struct csio_dma_buf *dma_buf;
25048c2ecf20Sopenharmony_ci
25058c2ecf20Sopenharmony_ci	while (!list_empty(&scm->ioreq_freelist)) {
25068c2ecf20Sopenharmony_ci		struct csio_sm *tmp;
25078c2ecf20Sopenharmony_ci
25088c2ecf20Sopenharmony_ci		tmp = list_first_entry(&scm->ioreq_freelist,
25098c2ecf20Sopenharmony_ci				       struct csio_sm, sm_list);
25108c2ecf20Sopenharmony_ci		list_del_init(&tmp->sm_list);
25118c2ecf20Sopenharmony_ci		ioreq = (struct csio_ioreq *)tmp;
25128c2ecf20Sopenharmony_ci
25138c2ecf20Sopenharmony_ci		dma_buf = &ioreq->dma_buf;
25148c2ecf20Sopenharmony_ci		dma_pool_free(scm->hw->scsi_dma_pool, dma_buf->vaddr,
25158c2ecf20Sopenharmony_ci			      dma_buf->paddr);
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci		kfree(ioreq);
25188c2ecf20Sopenharmony_ci	}
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	scm->stats.n_free_ioreq = 0;
25218c2ecf20Sopenharmony_ci
25228c2ecf20Sopenharmony_ci	csio_scsi_free_ddp_bufs(scm, scm->hw);
25238c2ecf20Sopenharmony_ci}
2524