162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2021 Broadcom. All Rights Reserved. The term
462306a36Sopenharmony_ci * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "efct_driver.h"
862306a36Sopenharmony_ci#include "efct_hw.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define enable_tsend_auto_resp(efct)	1
1162306a36Sopenharmony_ci#define enable_treceive_auto_resp(efct)	0
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define SCSI_IOFMT "[%04x][i:%04x t:%04x h:%04x]"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define scsi_io_printf(io, fmt, ...) \
1662306a36Sopenharmony_ci	efc_log_debug(io->efct, "[%s]" SCSI_IOFMT fmt, \
1762306a36Sopenharmony_ci		io->node->display_name, io->instance_index,\
1862306a36Sopenharmony_ci		io->init_task_tag, io->tgt_task_tag, io->hw_tag, ##__VA_ARGS__)
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define EFCT_LOG_ENABLE_SCSI_TRACE(efct)                \
2162306a36Sopenharmony_ci		(((efct) != NULL) ? (((efct)->logmask & (1U << 2)) != 0) : 0)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define scsi_io_trace(io, fmt, ...) \
2462306a36Sopenharmony_ci	do { \
2562306a36Sopenharmony_ci		if (EFCT_LOG_ENABLE_SCSI_TRACE(io->efct)) \
2662306a36Sopenharmony_ci			scsi_io_printf(io, fmt, ##__VA_ARGS__); \
2762306a36Sopenharmony_ci	} while (0)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct efct_io *
3062306a36Sopenharmony_ciefct_scsi_io_alloc(struct efct_node *node)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct efct *efct;
3362306a36Sopenharmony_ci	struct efct_xport *xport;
3462306a36Sopenharmony_ci	struct efct_io *io;
3562306a36Sopenharmony_ci	unsigned long flags;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	efct = node->efct;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	xport = efct->xport;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	io = efct_io_pool_io_alloc(efct->xport->io_pool);
4262306a36Sopenharmony_ci	if (!io) {
4362306a36Sopenharmony_ci		efc_log_err(efct, "IO alloc Failed\n");
4462306a36Sopenharmony_ci		atomic_add_return(1, &xport->io_alloc_failed_count);
4562306a36Sopenharmony_ci		return NULL;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* initialize refcount */
4962306a36Sopenharmony_ci	kref_init(&io->ref);
5062306a36Sopenharmony_ci	io->release = _efct_scsi_io_free;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* set generic fields */
5362306a36Sopenharmony_ci	io->efct = efct;
5462306a36Sopenharmony_ci	io->node = node;
5562306a36Sopenharmony_ci	kref_get(&node->ref);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/* set type and name */
5862306a36Sopenharmony_ci	io->io_type = EFCT_IO_TYPE_IO;
5962306a36Sopenharmony_ci	io->display_name = "scsi_io";
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	io->cmd_ini = false;
6262306a36Sopenharmony_ci	io->cmd_tgt = true;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* Add to node's active_ios list */
6562306a36Sopenharmony_ci	INIT_LIST_HEAD(&io->list_entry);
6662306a36Sopenharmony_ci	spin_lock_irqsave(&node->active_ios_lock, flags);
6762306a36Sopenharmony_ci	list_add(&io->list_entry, &node->active_ios);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	spin_unlock_irqrestore(&node->active_ios_lock, flags);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return io;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_civoid
7562306a36Sopenharmony_ci_efct_scsi_io_free(struct kref *arg)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct efct_io *io = container_of(arg, struct efct_io, ref);
7862306a36Sopenharmony_ci	struct efct *efct = io->efct;
7962306a36Sopenharmony_ci	struct efct_node *node = io->node;
8062306a36Sopenharmony_ci	unsigned long flags = 0;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (io->io_free) {
8562306a36Sopenharmony_ci		efc_log_err(efct, "IO already freed.\n");
8662306a36Sopenharmony_ci		return;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	spin_lock_irqsave(&node->active_ios_lock, flags);
9062306a36Sopenharmony_ci	list_del_init(&io->list_entry);
9162306a36Sopenharmony_ci	spin_unlock_irqrestore(&node->active_ios_lock, flags);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	kref_put(&node->ref, node->release);
9462306a36Sopenharmony_ci	io->node = NULL;
9562306a36Sopenharmony_ci	efct_io_pool_io_free(efct->xport->io_pool, io);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_civoid
9962306a36Sopenharmony_ciefct_scsi_io_free(struct efct_io *io)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
10262306a36Sopenharmony_ci	WARN_ON(!refcount_read(&io->ref.refcount));
10362306a36Sopenharmony_ci	kref_put(&io->ref, io->release);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void
10762306a36Sopenharmony_ciefct_target_io_cb(struct efct_hw_io *hio, u32 length, int status,
10862306a36Sopenharmony_ci		  u32 ext_status, void *app)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	u32 flags = 0;
11162306a36Sopenharmony_ci	struct efct_io *io = app;
11262306a36Sopenharmony_ci	struct efct *efct;
11362306a36Sopenharmony_ci	enum efct_scsi_io_status scsi_stat = EFCT_SCSI_STATUS_GOOD;
11462306a36Sopenharmony_ci	efct_scsi_io_cb_t cb;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (!io || !io->efct) {
11762306a36Sopenharmony_ci		pr_err("%s: IO can not be NULL\n", __func__);
11862306a36Sopenharmony_ci		return;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	scsi_io_trace(io, "status x%x ext_status x%x\n", status, ext_status);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	efct = io->efct;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	io->transferred += length;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (!io->scsi_tgt_cb) {
12862306a36Sopenharmony_ci		efct_scsi_check_pending(efct);
12962306a36Sopenharmony_ci		return;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* Call target server completion */
13362306a36Sopenharmony_ci	cb = io->scsi_tgt_cb;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* Clear the callback before invoking the callback */
13662306a36Sopenharmony_ci	io->scsi_tgt_cb = NULL;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* if status was good, and auto-good-response was set,
13962306a36Sopenharmony_ci	 * then callback target-server with IO_CMPL_RSP_SENT,
14062306a36Sopenharmony_ci	 * otherwise send IO_CMPL
14162306a36Sopenharmony_ci	 */
14262306a36Sopenharmony_ci	if (status == 0 && io->auto_resp)
14362306a36Sopenharmony_ci		flags |= EFCT_SCSI_IO_CMPL_RSP_SENT;
14462306a36Sopenharmony_ci	else
14562306a36Sopenharmony_ci		flags |= EFCT_SCSI_IO_CMPL;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	switch (status) {
14862306a36Sopenharmony_ci	case SLI4_FC_WCQE_STATUS_SUCCESS:
14962306a36Sopenharmony_ci		scsi_stat = EFCT_SCSI_STATUS_GOOD;
15062306a36Sopenharmony_ci		break;
15162306a36Sopenharmony_ci	case SLI4_FC_WCQE_STATUS_DI_ERROR:
15262306a36Sopenharmony_ci		if (ext_status & SLI4_FC_DI_ERROR_GE)
15362306a36Sopenharmony_ci			scsi_stat = EFCT_SCSI_STATUS_DIF_GUARD_ERR;
15462306a36Sopenharmony_ci		else if (ext_status & SLI4_FC_DI_ERROR_AE)
15562306a36Sopenharmony_ci			scsi_stat = EFCT_SCSI_STATUS_DIF_APP_TAG_ERROR;
15662306a36Sopenharmony_ci		else if (ext_status & SLI4_FC_DI_ERROR_RE)
15762306a36Sopenharmony_ci			scsi_stat = EFCT_SCSI_STATUS_DIF_REF_TAG_ERROR;
15862306a36Sopenharmony_ci		else
15962306a36Sopenharmony_ci			scsi_stat = EFCT_SCSI_STATUS_DIF_UNKNOWN_ERROR;
16062306a36Sopenharmony_ci		break;
16162306a36Sopenharmony_ci	case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
16262306a36Sopenharmony_ci		switch (ext_status) {
16362306a36Sopenharmony_ci		case SLI4_FC_LOCAL_REJECT_INVALID_RELOFFSET:
16462306a36Sopenharmony_ci		case SLI4_FC_LOCAL_REJECT_ABORT_REQUESTED:
16562306a36Sopenharmony_ci			scsi_stat = EFCT_SCSI_STATUS_ABORTED;
16662306a36Sopenharmony_ci			break;
16762306a36Sopenharmony_ci		case SLI4_FC_LOCAL_REJECT_INVALID_RPI:
16862306a36Sopenharmony_ci			scsi_stat = EFCT_SCSI_STATUS_NEXUS_LOST;
16962306a36Sopenharmony_ci			break;
17062306a36Sopenharmony_ci		case SLI4_FC_LOCAL_REJECT_NO_XRI:
17162306a36Sopenharmony_ci			scsi_stat = EFCT_SCSI_STATUS_NO_IO;
17262306a36Sopenharmony_ci			break;
17362306a36Sopenharmony_ci		default:
17462306a36Sopenharmony_ci			/*we have seen 0x0d(TX_DMA_FAILED err)*/
17562306a36Sopenharmony_ci			scsi_stat = EFCT_SCSI_STATUS_ERROR;
17662306a36Sopenharmony_ci			break;
17762306a36Sopenharmony_ci		}
17862306a36Sopenharmony_ci		break;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	case SLI4_FC_WCQE_STATUS_TARGET_WQE_TIMEOUT:
18162306a36Sopenharmony_ci		/* target IO timed out */
18262306a36Sopenharmony_ci		scsi_stat = EFCT_SCSI_STATUS_TIMEDOUT_AND_ABORTED;
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	case SLI4_FC_WCQE_STATUS_SHUTDOWN:
18662306a36Sopenharmony_ci		/* Target IO cancelled by HW */
18762306a36Sopenharmony_ci		scsi_stat = EFCT_SCSI_STATUS_SHUTDOWN;
18862306a36Sopenharmony_ci		break;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	default:
19162306a36Sopenharmony_ci		scsi_stat = EFCT_SCSI_STATUS_ERROR;
19262306a36Sopenharmony_ci		break;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	cb(io, scsi_stat, flags, io->scsi_tgt_cb_arg);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	efct_scsi_check_pending(efct);
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int
20162306a36Sopenharmony_ciefct_scsi_build_sgls(struct efct_hw *hw, struct efct_hw_io *hio,
20262306a36Sopenharmony_ci		     struct efct_scsi_sgl *sgl, u32 sgl_count,
20362306a36Sopenharmony_ci		     enum efct_hw_io_type type)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	int rc;
20662306a36Sopenharmony_ci	u32 i;
20762306a36Sopenharmony_ci	struct efct *efct = hw->os;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* Initialize HW SGL */
21062306a36Sopenharmony_ci	rc = efct_hw_io_init_sges(hw, hio, type);
21162306a36Sopenharmony_ci	if (rc) {
21262306a36Sopenharmony_ci		efc_log_err(efct, "efct_hw_io_init_sges failed: %d\n", rc);
21362306a36Sopenharmony_ci		return -EIO;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	for (i = 0; i < sgl_count; i++) {
21762306a36Sopenharmony_ci		/* Add data SGE */
21862306a36Sopenharmony_ci		rc = efct_hw_io_add_sge(hw, hio, sgl[i].addr, sgl[i].len);
21962306a36Sopenharmony_ci		if (rc) {
22062306a36Sopenharmony_ci			efc_log_err(efct, "add sge failed cnt=%d rc=%d\n",
22162306a36Sopenharmony_ci				    sgl_count, rc);
22262306a36Sopenharmony_ci			return rc;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return 0;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void efc_log_sgl(struct efct_io *io)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct efct_hw_io *hio = io->hio;
23262306a36Sopenharmony_ci	struct sli4_sge *data = NULL;
23362306a36Sopenharmony_ci	u32 *dword = NULL;
23462306a36Sopenharmony_ci	u32 i;
23562306a36Sopenharmony_ci	u32 n_sge;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	scsi_io_trace(io, "def_sgl at 0x%x 0x%08x\n",
23862306a36Sopenharmony_ci		      upper_32_bits(hio->def_sgl.phys),
23962306a36Sopenharmony_ci		      lower_32_bits(hio->def_sgl.phys));
24062306a36Sopenharmony_ci	n_sge = (hio->sgl == &hio->def_sgl) ? hio->n_sge : hio->def_sgl_count;
24162306a36Sopenharmony_ci	for (i = 0, data = hio->def_sgl.virt; i < n_sge; i++, data++) {
24262306a36Sopenharmony_ci		dword = (u32 *)data;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		scsi_io_trace(io, "SGL %2d 0x%08x 0x%08x 0x%08x 0x%08x\n",
24562306a36Sopenharmony_ci			      i, dword[0], dword[1], dword[2], dword[3]);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		if (dword[2] & (1U << 31))
24862306a36Sopenharmony_ci			break;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic void
25362306a36Sopenharmony_ciefct_scsi_check_pending_async_cb(struct efct_hw *hw, int status,
25462306a36Sopenharmony_ci				 u8 *mqe, void *arg)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	struct efct_io *io = arg;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (io) {
25962306a36Sopenharmony_ci		efct_hw_done_t cb = io->hw_cb;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci		if (!io->hw_cb)
26262306a36Sopenharmony_ci			return;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		io->hw_cb = NULL;
26562306a36Sopenharmony_ci		(cb)(io->hio, 0, SLI4_FC_WCQE_STATUS_DISPATCH_ERROR, 0, io);
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic int
27062306a36Sopenharmony_ciefct_scsi_io_dispatch_hw_io(struct efct_io *io, struct efct_hw_io *hio)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	int rc = 0;
27362306a36Sopenharmony_ci	struct efct *efct = io->efct;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* Got a HW IO;
27662306a36Sopenharmony_ci	 * update ini/tgt_task_tag with HW IO info and dispatch
27762306a36Sopenharmony_ci	 */
27862306a36Sopenharmony_ci	io->hio = hio;
27962306a36Sopenharmony_ci	if (io->cmd_tgt)
28062306a36Sopenharmony_ci		io->tgt_task_tag = hio->indicator;
28162306a36Sopenharmony_ci	else if (io->cmd_ini)
28262306a36Sopenharmony_ci		io->init_task_tag = hio->indicator;
28362306a36Sopenharmony_ci	io->hw_tag = hio->reqtag;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	hio->eq = io->hw_priv;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* Copy WQ steering */
28862306a36Sopenharmony_ci	switch (io->wq_steering) {
28962306a36Sopenharmony_ci	case EFCT_SCSI_WQ_STEERING_CLASS >> EFCT_SCSI_WQ_STEERING_SHIFT:
29062306a36Sopenharmony_ci		hio->wq_steering = EFCT_HW_WQ_STEERING_CLASS;
29162306a36Sopenharmony_ci		break;
29262306a36Sopenharmony_ci	case EFCT_SCSI_WQ_STEERING_REQUEST >> EFCT_SCSI_WQ_STEERING_SHIFT:
29362306a36Sopenharmony_ci		hio->wq_steering = EFCT_HW_WQ_STEERING_REQUEST;
29462306a36Sopenharmony_ci		break;
29562306a36Sopenharmony_ci	case EFCT_SCSI_WQ_STEERING_CPU >> EFCT_SCSI_WQ_STEERING_SHIFT:
29662306a36Sopenharmony_ci		hio->wq_steering = EFCT_HW_WQ_STEERING_CPU;
29762306a36Sopenharmony_ci		break;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	switch (io->io_type) {
30162306a36Sopenharmony_ci	case EFCT_IO_TYPE_IO:
30262306a36Sopenharmony_ci		rc = efct_scsi_build_sgls(&efct->hw, io->hio,
30362306a36Sopenharmony_ci					  io->sgl, io->sgl_count, io->hio_type);
30462306a36Sopenharmony_ci		if (rc)
30562306a36Sopenharmony_ci			break;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		if (EFCT_LOG_ENABLE_SCSI_TRACE(efct))
30862306a36Sopenharmony_ci			efc_log_sgl(io);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci		if (io->app_id)
31162306a36Sopenharmony_ci			io->iparam.fcp_tgt.app_id = io->app_id;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		io->iparam.fcp_tgt.vpi = io->node->vpi;
31462306a36Sopenharmony_ci		io->iparam.fcp_tgt.rpi = io->node->rpi;
31562306a36Sopenharmony_ci		io->iparam.fcp_tgt.s_id = io->node->port_fc_id;
31662306a36Sopenharmony_ci		io->iparam.fcp_tgt.d_id = io->node->node_fc_id;
31762306a36Sopenharmony_ci		io->iparam.fcp_tgt.xmit_len = io->wire_len;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		rc = efct_hw_io_send(&io->efct->hw, io->hio_type, io->hio,
32062306a36Sopenharmony_ci				     &io->iparam, io->hw_cb, io);
32162306a36Sopenharmony_ci		break;
32262306a36Sopenharmony_ci	default:
32362306a36Sopenharmony_ci		scsi_io_printf(io, "Unknown IO type=%d\n", io->io_type);
32462306a36Sopenharmony_ci		rc = -EIO;
32562306a36Sopenharmony_ci		break;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci	return rc;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int
33162306a36Sopenharmony_ciefct_scsi_io_dispatch_no_hw_io(struct efct_io *io)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	int rc;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	switch (io->io_type) {
33662306a36Sopenharmony_ci	case EFCT_IO_TYPE_ABORT: {
33762306a36Sopenharmony_ci		struct efct_hw_io *hio_to_abort = NULL;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		hio_to_abort = io->io_to_abort->hio;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		if (!hio_to_abort) {
34262306a36Sopenharmony_ci			/*
34362306a36Sopenharmony_ci			 * If "IO to abort" does not have an
34462306a36Sopenharmony_ci			 * associated HW IO, immediately make callback with
34562306a36Sopenharmony_ci			 * success. The command must have been sent to
34662306a36Sopenharmony_ci			 * the backend, but the data phase has not yet
34762306a36Sopenharmony_ci			 * started, so we don't have a HW IO.
34862306a36Sopenharmony_ci			 *
34962306a36Sopenharmony_ci			 * Note: since the backend shims should be
35062306a36Sopenharmony_ci			 * taking a reference on io_to_abort, it should not
35162306a36Sopenharmony_ci			 * be possible to have been completed and freed by
35262306a36Sopenharmony_ci			 * the backend before the abort got here.
35362306a36Sopenharmony_ci			 */
35462306a36Sopenharmony_ci			scsi_io_printf(io, "IO: not active\n");
35562306a36Sopenharmony_ci			((efct_hw_done_t)io->hw_cb)(io->hio, 0,
35662306a36Sopenharmony_ci					SLI4_FC_WCQE_STATUS_SUCCESS, 0, io);
35762306a36Sopenharmony_ci			rc = 0;
35862306a36Sopenharmony_ci			break;
35962306a36Sopenharmony_ci		}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		/* HW IO is valid, abort it */
36262306a36Sopenharmony_ci		scsi_io_printf(io, "aborting\n");
36362306a36Sopenharmony_ci		rc = efct_hw_io_abort(&io->efct->hw, hio_to_abort,
36462306a36Sopenharmony_ci				      io->send_abts, io->hw_cb, io);
36562306a36Sopenharmony_ci		if (rc) {
36662306a36Sopenharmony_ci			int status = SLI4_FC_WCQE_STATUS_SUCCESS;
36762306a36Sopenharmony_ci			efct_hw_done_t cb = io->hw_cb;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci			if (rc != -ENOENT && rc != -EINPROGRESS) {
37062306a36Sopenharmony_ci				status = -1;
37162306a36Sopenharmony_ci				scsi_io_printf(io, "Failed to abort IO rc=%d\n",
37262306a36Sopenharmony_ci					       rc);
37362306a36Sopenharmony_ci			}
37462306a36Sopenharmony_ci			cb(io->hio, 0, status, 0, io);
37562306a36Sopenharmony_ci			rc = 0;
37662306a36Sopenharmony_ci		}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		break;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci	default:
38162306a36Sopenharmony_ci		scsi_io_printf(io, "Unknown IO type=%d\n", io->io_type);
38262306a36Sopenharmony_ci		rc = -EIO;
38362306a36Sopenharmony_ci		break;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci	return rc;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic struct efct_io *
38962306a36Sopenharmony_ciefct_scsi_dispatch_pending(struct efct *efct)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct efct_xport *xport = efct->xport;
39262306a36Sopenharmony_ci	struct efct_io *io = NULL;
39362306a36Sopenharmony_ci	struct efct_hw_io *hio;
39462306a36Sopenharmony_ci	unsigned long flags = 0;
39562306a36Sopenharmony_ci	int status;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	spin_lock_irqsave(&xport->io_pending_lock, flags);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (!list_empty(&xport->io_pending_list)) {
40062306a36Sopenharmony_ci		io = list_first_entry(&xport->io_pending_list, struct efct_io,
40162306a36Sopenharmony_ci				      io_pending_link);
40262306a36Sopenharmony_ci		list_del_init(&io->io_pending_link);
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!io) {
40662306a36Sopenharmony_ci		spin_unlock_irqrestore(&xport->io_pending_lock, flags);
40762306a36Sopenharmony_ci		return NULL;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (io->io_type == EFCT_IO_TYPE_ABORT) {
41162306a36Sopenharmony_ci		hio = NULL;
41262306a36Sopenharmony_ci	} else {
41362306a36Sopenharmony_ci		hio = efct_hw_io_alloc(&efct->hw);
41462306a36Sopenharmony_ci		if (!hio) {
41562306a36Sopenharmony_ci			/*
41662306a36Sopenharmony_ci			 * No HW IO available.Put IO back on
41762306a36Sopenharmony_ci			 * the front of pending list
41862306a36Sopenharmony_ci			 */
41962306a36Sopenharmony_ci			list_add(&xport->io_pending_list, &io->io_pending_link);
42062306a36Sopenharmony_ci			io = NULL;
42162306a36Sopenharmony_ci		} else {
42262306a36Sopenharmony_ci			hio->eq = io->hw_priv;
42362306a36Sopenharmony_ci		}
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* Must drop the lock before dispatching the IO */
42762306a36Sopenharmony_ci	spin_unlock_irqrestore(&xport->io_pending_lock, flags);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (!io)
43062306a36Sopenharmony_ci		return NULL;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/*
43362306a36Sopenharmony_ci	 * We pulled an IO off the pending list,
43462306a36Sopenharmony_ci	 * and either got an HW IO or don't need one
43562306a36Sopenharmony_ci	 */
43662306a36Sopenharmony_ci	atomic_sub_return(1, &xport->io_pending_count);
43762306a36Sopenharmony_ci	if (!hio)
43862306a36Sopenharmony_ci		status = efct_scsi_io_dispatch_no_hw_io(io);
43962306a36Sopenharmony_ci	else
44062306a36Sopenharmony_ci		status = efct_scsi_io_dispatch_hw_io(io, hio);
44162306a36Sopenharmony_ci	if (status) {
44262306a36Sopenharmony_ci		/*
44362306a36Sopenharmony_ci		 * Invoke the HW callback, but do so in the
44462306a36Sopenharmony_ci		 * separate execution context,provided by the
44562306a36Sopenharmony_ci		 * NOP mailbox completion processing context
44662306a36Sopenharmony_ci		 * by using efct_hw_async_call()
44762306a36Sopenharmony_ci		 */
44862306a36Sopenharmony_ci		if (efct_hw_async_call(&efct->hw,
44962306a36Sopenharmony_ci				       efct_scsi_check_pending_async_cb, io)) {
45062306a36Sopenharmony_ci			efc_log_debug(efct, "call hw async failed\n");
45162306a36Sopenharmony_ci		}
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return io;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_civoid
45862306a36Sopenharmony_ciefct_scsi_check_pending(struct efct *efct)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct efct_xport *xport = efct->xport;
46162306a36Sopenharmony_ci	struct efct_io *io = NULL;
46262306a36Sopenharmony_ci	int count = 0;
46362306a36Sopenharmony_ci	unsigned long flags = 0;
46462306a36Sopenharmony_ci	int dispatch = 0;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/* Guard against recursion */
46762306a36Sopenharmony_ci	if (atomic_add_return(1, &xport->io_pending_recursing)) {
46862306a36Sopenharmony_ci		/* This function is already running.  Decrement and return. */
46962306a36Sopenharmony_ci		atomic_sub_return(1, &xport->io_pending_recursing);
47062306a36Sopenharmony_ci		return;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	while (efct_scsi_dispatch_pending(efct))
47462306a36Sopenharmony_ci		count++;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	if (count) {
47762306a36Sopenharmony_ci		atomic_sub_return(1, &xport->io_pending_recursing);
47862306a36Sopenharmony_ci		return;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/*
48262306a36Sopenharmony_ci	 * If nothing was removed from the list,
48362306a36Sopenharmony_ci	 * we might be in a case where we need to abort an
48462306a36Sopenharmony_ci	 * active IO and the abort is on the pending list.
48562306a36Sopenharmony_ci	 * Look for an abort we can dispatch.
48662306a36Sopenharmony_ci	 */
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	spin_lock_irqsave(&xport->io_pending_lock, flags);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	list_for_each_entry(io, &xport->io_pending_list, io_pending_link) {
49162306a36Sopenharmony_ci		if (io->io_type == EFCT_IO_TYPE_ABORT && io->io_to_abort->hio) {
49262306a36Sopenharmony_ci			/* This IO has a HW IO, so it is
49362306a36Sopenharmony_ci			 * active.  Dispatch the abort.
49462306a36Sopenharmony_ci			 */
49562306a36Sopenharmony_ci			dispatch = 1;
49662306a36Sopenharmony_ci			list_del_init(&io->io_pending_link);
49762306a36Sopenharmony_ci			atomic_sub_return(1, &xport->io_pending_count);
49862306a36Sopenharmony_ci			break;
49962306a36Sopenharmony_ci		}
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	spin_unlock_irqrestore(&xport->io_pending_lock, flags);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	if (dispatch) {
50562306a36Sopenharmony_ci		if (efct_scsi_io_dispatch_no_hw_io(io)) {
50662306a36Sopenharmony_ci			if (efct_hw_async_call(&efct->hw,
50762306a36Sopenharmony_ci				efct_scsi_check_pending_async_cb, io)) {
50862306a36Sopenharmony_ci				efc_log_debug(efct, "hw async failed\n");
50962306a36Sopenharmony_ci			}
51062306a36Sopenharmony_ci		}
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	atomic_sub_return(1, &xport->io_pending_recursing);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ciint
51762306a36Sopenharmony_ciefct_scsi_io_dispatch(struct efct_io *io, void *cb)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	struct efct_hw_io *hio;
52062306a36Sopenharmony_ci	struct efct *efct = io->efct;
52162306a36Sopenharmony_ci	struct efct_xport *xport = efct->xport;
52262306a36Sopenharmony_ci	unsigned long flags = 0;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	io->hw_cb = cb;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/*
52762306a36Sopenharmony_ci	 * if this IO already has a HW IO, then this is either
52862306a36Sopenharmony_ci	 * not the first phase of the IO. Send it to the HW.
52962306a36Sopenharmony_ci	 */
53062306a36Sopenharmony_ci	if (io->hio)
53162306a36Sopenharmony_ci		return efct_scsi_io_dispatch_hw_io(io, io->hio);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	/*
53462306a36Sopenharmony_ci	 * We don't already have a HW IO associated with the IO. First check
53562306a36Sopenharmony_ci	 * the pending list. If not empty, add IO to the tail and process the
53662306a36Sopenharmony_ci	 * pending list.
53762306a36Sopenharmony_ci	 */
53862306a36Sopenharmony_ci	spin_lock_irqsave(&xport->io_pending_lock, flags);
53962306a36Sopenharmony_ci	if (!list_empty(&xport->io_pending_list)) {
54062306a36Sopenharmony_ci		/*
54162306a36Sopenharmony_ci		 * If this is a low latency request,
54262306a36Sopenharmony_ci		 * the put at the front of the IO pending
54362306a36Sopenharmony_ci		 * queue, otherwise put it at the end of the queue.
54462306a36Sopenharmony_ci		 */
54562306a36Sopenharmony_ci		if (io->low_latency) {
54662306a36Sopenharmony_ci			INIT_LIST_HEAD(&io->io_pending_link);
54762306a36Sopenharmony_ci			list_add(&xport->io_pending_list, &io->io_pending_link);
54862306a36Sopenharmony_ci		} else {
54962306a36Sopenharmony_ci			INIT_LIST_HEAD(&io->io_pending_link);
55062306a36Sopenharmony_ci			list_add_tail(&io->io_pending_link,
55162306a36Sopenharmony_ci				      &xport->io_pending_list);
55262306a36Sopenharmony_ci		}
55362306a36Sopenharmony_ci		spin_unlock_irqrestore(&xport->io_pending_lock, flags);
55462306a36Sopenharmony_ci		atomic_add_return(1, &xport->io_pending_count);
55562306a36Sopenharmony_ci		atomic_add_return(1, &xport->io_total_pending);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		/* process pending list */
55862306a36Sopenharmony_ci		efct_scsi_check_pending(efct);
55962306a36Sopenharmony_ci		return 0;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci	spin_unlock_irqrestore(&xport->io_pending_lock, flags);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	/*
56462306a36Sopenharmony_ci	 * We don't have a HW IO associated with the IO and there's nothing
56562306a36Sopenharmony_ci	 * on the pending list. Attempt to allocate a HW IO and dispatch it.
56662306a36Sopenharmony_ci	 */
56762306a36Sopenharmony_ci	hio = efct_hw_io_alloc(&io->efct->hw);
56862306a36Sopenharmony_ci	if (!hio) {
56962306a36Sopenharmony_ci		/* Couldn't get a HW IO. Save this IO on the pending list */
57062306a36Sopenharmony_ci		spin_lock_irqsave(&xport->io_pending_lock, flags);
57162306a36Sopenharmony_ci		INIT_LIST_HEAD(&io->io_pending_link);
57262306a36Sopenharmony_ci		list_add_tail(&io->io_pending_link, &xport->io_pending_list);
57362306a36Sopenharmony_ci		spin_unlock_irqrestore(&xport->io_pending_lock, flags);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		atomic_add_return(1, &xport->io_total_pending);
57662306a36Sopenharmony_ci		atomic_add_return(1, &xport->io_pending_count);
57762306a36Sopenharmony_ci		return 0;
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/* We successfully allocated a HW IO; dispatch to HW */
58162306a36Sopenharmony_ci	return efct_scsi_io_dispatch_hw_io(io, hio);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ciint
58562306a36Sopenharmony_ciefct_scsi_io_dispatch_abort(struct efct_io *io, void *cb)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	struct efct *efct = io->efct;
58862306a36Sopenharmony_ci	struct efct_xport *xport = efct->xport;
58962306a36Sopenharmony_ci	unsigned long flags = 0;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	io->hw_cb = cb;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	/*
59462306a36Sopenharmony_ci	 * For aborts, we don't need a HW IO, but we still want
59562306a36Sopenharmony_ci	 * to pass through the pending list to preserve ordering.
59662306a36Sopenharmony_ci	 * Thus, if the pending list is not empty, add this abort
59762306a36Sopenharmony_ci	 * to the pending list and process the pending list.
59862306a36Sopenharmony_ci	 */
59962306a36Sopenharmony_ci	spin_lock_irqsave(&xport->io_pending_lock, flags);
60062306a36Sopenharmony_ci	if (!list_empty(&xport->io_pending_list)) {
60162306a36Sopenharmony_ci		INIT_LIST_HEAD(&io->io_pending_link);
60262306a36Sopenharmony_ci		list_add_tail(&io->io_pending_link, &xport->io_pending_list);
60362306a36Sopenharmony_ci		spin_unlock_irqrestore(&xport->io_pending_lock, flags);
60462306a36Sopenharmony_ci		atomic_add_return(1, &xport->io_pending_count);
60562306a36Sopenharmony_ci		atomic_add_return(1, &xport->io_total_pending);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci		/* process pending list */
60862306a36Sopenharmony_ci		efct_scsi_check_pending(efct);
60962306a36Sopenharmony_ci		return 0;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci	spin_unlock_irqrestore(&xport->io_pending_lock, flags);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/* nothing on pending list, dispatch abort */
61462306a36Sopenharmony_ci	return efct_scsi_io_dispatch_no_hw_io(io);
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic inline int
61862306a36Sopenharmony_ciefct_scsi_xfer_data(struct efct_io *io, u32 flags,
61962306a36Sopenharmony_ci		    struct efct_scsi_sgl *sgl, u32 sgl_count, u64 xwire_len,
62062306a36Sopenharmony_ci		    enum efct_hw_io_type type, int enable_ar,
62162306a36Sopenharmony_ci		    efct_scsi_io_cb_t cb, void *arg)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	struct efct *efct;
62462306a36Sopenharmony_ci	size_t residual = 0;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	io->sgl_count = sgl_count;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	efct = io->efct;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	scsi_io_trace(io, "%s wire_len %llu\n",
63162306a36Sopenharmony_ci		      (type == EFCT_HW_IO_TARGET_READ) ? "send" : "recv",
63262306a36Sopenharmony_ci		      xwire_len);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	io->hio_type = type;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	io->scsi_tgt_cb = cb;
63762306a36Sopenharmony_ci	io->scsi_tgt_cb_arg = arg;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	residual = io->exp_xfer_len - io->transferred;
64062306a36Sopenharmony_ci	io->wire_len = (xwire_len < residual) ? xwire_len : residual;
64162306a36Sopenharmony_ci	residual = (xwire_len - io->wire_len);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	memset(&io->iparam, 0, sizeof(io->iparam));
64462306a36Sopenharmony_ci	io->iparam.fcp_tgt.ox_id = io->init_task_tag;
64562306a36Sopenharmony_ci	io->iparam.fcp_tgt.offset = io->transferred;
64662306a36Sopenharmony_ci	io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
64762306a36Sopenharmony_ci	io->iparam.fcp_tgt.timeout = io->timeout;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	/* if this is the last data phase and there is no residual, enable
65062306a36Sopenharmony_ci	 * auto-good-response
65162306a36Sopenharmony_ci	 */
65262306a36Sopenharmony_ci	if (enable_ar && (flags & EFCT_SCSI_LAST_DATAPHASE) && residual == 0 &&
65362306a36Sopenharmony_ci	    ((io->transferred + io->wire_len) == io->exp_xfer_len) &&
65462306a36Sopenharmony_ci	    (!(flags & EFCT_SCSI_NO_AUTO_RESPONSE))) {
65562306a36Sopenharmony_ci		io->iparam.fcp_tgt.flags |= SLI4_IO_AUTO_GOOD_RESPONSE;
65662306a36Sopenharmony_ci		io->auto_resp = true;
65762306a36Sopenharmony_ci	} else {
65862306a36Sopenharmony_ci		io->auto_resp = false;
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	/* save this transfer length */
66262306a36Sopenharmony_ci	io->xfer_req = io->wire_len;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	/* Adjust the transferred count to account for overrun
66562306a36Sopenharmony_ci	 * when the residual is calculated in efct_scsi_send_resp
66662306a36Sopenharmony_ci	 */
66762306a36Sopenharmony_ci	io->transferred += residual;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	/* Adjust the SGL size if there is overrun */
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	if (residual) {
67262306a36Sopenharmony_ci		struct efct_scsi_sgl  *sgl_ptr = &io->sgl[sgl_count - 1];
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci		while (residual) {
67562306a36Sopenharmony_ci			size_t len = sgl_ptr->len;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci			if (len > residual) {
67862306a36Sopenharmony_ci				sgl_ptr->len = len - residual;
67962306a36Sopenharmony_ci				residual = 0;
68062306a36Sopenharmony_ci			} else {
68162306a36Sopenharmony_ci				sgl_ptr->len = 0;
68262306a36Sopenharmony_ci				residual -= len;
68362306a36Sopenharmony_ci				io->sgl_count--;
68462306a36Sopenharmony_ci			}
68562306a36Sopenharmony_ci			sgl_ptr--;
68662306a36Sopenharmony_ci		}
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	/* Set latency and WQ steering */
69062306a36Sopenharmony_ci	io->low_latency = (flags & EFCT_SCSI_LOW_LATENCY) != 0;
69162306a36Sopenharmony_ci	io->wq_steering = (flags & EFCT_SCSI_WQ_STEERING_MASK) >>
69262306a36Sopenharmony_ci				EFCT_SCSI_WQ_STEERING_SHIFT;
69362306a36Sopenharmony_ci	io->wq_class = (flags & EFCT_SCSI_WQ_CLASS_MASK) >>
69462306a36Sopenharmony_ci				EFCT_SCSI_WQ_CLASS_SHIFT;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (efct->xport) {
69762306a36Sopenharmony_ci		struct efct_xport *xport = efct->xport;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci		if (type == EFCT_HW_IO_TARGET_READ) {
70062306a36Sopenharmony_ci			xport->fcp_stats.input_requests++;
70162306a36Sopenharmony_ci			xport->fcp_stats.input_bytes += xwire_len;
70262306a36Sopenharmony_ci		} else if (type == EFCT_HW_IO_TARGET_WRITE) {
70362306a36Sopenharmony_ci			xport->fcp_stats.output_requests++;
70462306a36Sopenharmony_ci			xport->fcp_stats.output_bytes += xwire_len;
70562306a36Sopenharmony_ci		}
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci	return efct_scsi_io_dispatch(io, efct_target_io_cb);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ciint
71162306a36Sopenharmony_ciefct_scsi_send_rd_data(struct efct_io *io, u32 flags,
71262306a36Sopenharmony_ci		       struct efct_scsi_sgl *sgl, u32 sgl_count, u64 len,
71362306a36Sopenharmony_ci		       efct_scsi_io_cb_t cb, void *arg)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	return efct_scsi_xfer_data(io, flags, sgl, sgl_count,
71662306a36Sopenharmony_ci				   len, EFCT_HW_IO_TARGET_READ,
71762306a36Sopenharmony_ci				   enable_tsend_auto_resp(io->efct), cb, arg);
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ciint
72162306a36Sopenharmony_ciefct_scsi_recv_wr_data(struct efct_io *io, u32 flags,
72262306a36Sopenharmony_ci		       struct efct_scsi_sgl *sgl, u32 sgl_count, u64 len,
72362306a36Sopenharmony_ci		       efct_scsi_io_cb_t cb, void *arg)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	return efct_scsi_xfer_data(io, flags, sgl, sgl_count, len,
72662306a36Sopenharmony_ci				   EFCT_HW_IO_TARGET_WRITE,
72762306a36Sopenharmony_ci				   enable_treceive_auto_resp(io->efct), cb, arg);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ciint
73162306a36Sopenharmony_ciefct_scsi_send_resp(struct efct_io *io, u32 flags,
73262306a36Sopenharmony_ci		    struct efct_scsi_cmd_resp *rsp,
73362306a36Sopenharmony_ci		    efct_scsi_io_cb_t cb, void *arg)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	struct efct *efct;
73662306a36Sopenharmony_ci	int residual;
73762306a36Sopenharmony_ci	/* Always try auto resp */
73862306a36Sopenharmony_ci	bool auto_resp = true;
73962306a36Sopenharmony_ci	u8 scsi_status = 0;
74062306a36Sopenharmony_ci	u16 scsi_status_qualifier = 0;
74162306a36Sopenharmony_ci	u8 *sense_data = NULL;
74262306a36Sopenharmony_ci	u32 sense_data_length = 0;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	efct = io->efct;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	if (rsp) {
74762306a36Sopenharmony_ci		scsi_status = rsp->scsi_status;
74862306a36Sopenharmony_ci		scsi_status_qualifier = rsp->scsi_status_qualifier;
74962306a36Sopenharmony_ci		sense_data = rsp->sense_data;
75062306a36Sopenharmony_ci		sense_data_length = rsp->sense_data_length;
75162306a36Sopenharmony_ci		residual = rsp->residual;
75262306a36Sopenharmony_ci	} else {
75362306a36Sopenharmony_ci		residual = io->exp_xfer_len - io->transferred;
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	io->wire_len = 0;
75762306a36Sopenharmony_ci	io->hio_type = EFCT_HW_IO_TARGET_RSP;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	io->scsi_tgt_cb = cb;
76062306a36Sopenharmony_ci	io->scsi_tgt_cb_arg = arg;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	memset(&io->iparam, 0, sizeof(io->iparam));
76362306a36Sopenharmony_ci	io->iparam.fcp_tgt.ox_id = io->init_task_tag;
76462306a36Sopenharmony_ci	io->iparam.fcp_tgt.offset = 0;
76562306a36Sopenharmony_ci	io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
76662306a36Sopenharmony_ci	io->iparam.fcp_tgt.timeout = io->timeout;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	/* Set low latency queueing request */
76962306a36Sopenharmony_ci	io->low_latency = (flags & EFCT_SCSI_LOW_LATENCY) != 0;
77062306a36Sopenharmony_ci	io->wq_steering = (flags & EFCT_SCSI_WQ_STEERING_MASK) >>
77162306a36Sopenharmony_ci				EFCT_SCSI_WQ_STEERING_SHIFT;
77262306a36Sopenharmony_ci	io->wq_class = (flags & EFCT_SCSI_WQ_CLASS_MASK) >>
77362306a36Sopenharmony_ci				EFCT_SCSI_WQ_CLASS_SHIFT;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	if (scsi_status != 0 || residual || sense_data_length) {
77662306a36Sopenharmony_ci		struct fcp_resp_with_ext *fcprsp = io->rspbuf.virt;
77762306a36Sopenharmony_ci		u8 *sns_data;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		if (!fcprsp) {
78062306a36Sopenharmony_ci			efc_log_err(efct, "NULL response buffer\n");
78162306a36Sopenharmony_ci			return -EIO;
78262306a36Sopenharmony_ci		}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		sns_data = (u8 *)io->rspbuf.virt + sizeof(*fcprsp);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci		auto_resp = false;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci		memset(fcprsp, 0, sizeof(*fcprsp));
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		io->wire_len += sizeof(*fcprsp);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci		fcprsp->resp.fr_status = scsi_status;
79362306a36Sopenharmony_ci		fcprsp->resp.fr_retry_delay =
79462306a36Sopenharmony_ci			cpu_to_be16(scsi_status_qualifier);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci		/* set residual status if necessary */
79762306a36Sopenharmony_ci		if (residual != 0) {
79862306a36Sopenharmony_ci			/* FCP: if data transferred is less than the
79962306a36Sopenharmony_ci			 * amount expected, then this is an underflow.
80062306a36Sopenharmony_ci			 * If data transferred would have been greater
80162306a36Sopenharmony_ci			 * than the amount expected this is an overflow
80262306a36Sopenharmony_ci			 */
80362306a36Sopenharmony_ci			if (residual > 0) {
80462306a36Sopenharmony_ci				fcprsp->resp.fr_flags |= FCP_RESID_UNDER;
80562306a36Sopenharmony_ci				fcprsp->ext.fr_resid =	cpu_to_be32(residual);
80662306a36Sopenharmony_ci			} else {
80762306a36Sopenharmony_ci				fcprsp->resp.fr_flags |= FCP_RESID_OVER;
80862306a36Sopenharmony_ci				fcprsp->ext.fr_resid = cpu_to_be32(-residual);
80962306a36Sopenharmony_ci			}
81062306a36Sopenharmony_ci		}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci		if (EFCT_SCSI_SNS_BUF_VALID(sense_data) && sense_data_length) {
81362306a36Sopenharmony_ci			if (sense_data_length > SCSI_SENSE_BUFFERSIZE) {
81462306a36Sopenharmony_ci				efc_log_err(efct, "Sense exceeds max size.\n");
81562306a36Sopenharmony_ci				return -EIO;
81662306a36Sopenharmony_ci			}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci			fcprsp->resp.fr_flags |= FCP_SNS_LEN_VAL;
81962306a36Sopenharmony_ci			memcpy(sns_data, sense_data, sense_data_length);
82062306a36Sopenharmony_ci			fcprsp->ext.fr_sns_len = cpu_to_be32(sense_data_length);
82162306a36Sopenharmony_ci			io->wire_len += sense_data_length;
82262306a36Sopenharmony_ci		}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci		io->sgl[0].addr = io->rspbuf.phys;
82562306a36Sopenharmony_ci		io->sgl[0].dif_addr = 0;
82662306a36Sopenharmony_ci		io->sgl[0].len = io->wire_len;
82762306a36Sopenharmony_ci		io->sgl_count = 1;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	if (auto_resp)
83162306a36Sopenharmony_ci		io->iparam.fcp_tgt.flags |= SLI4_IO_AUTO_GOOD_RESPONSE;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	return efct_scsi_io_dispatch(io, efct_target_io_cb);
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic int
83762306a36Sopenharmony_ciefct_target_bls_resp_cb(struct efct_hw_io *hio,	u32 length, int status,
83862306a36Sopenharmony_ci			u32 ext_status, void *app)
83962306a36Sopenharmony_ci{
84062306a36Sopenharmony_ci	struct efct_io *io = app;
84162306a36Sopenharmony_ci	struct efct *efct;
84262306a36Sopenharmony_ci	enum efct_scsi_io_status bls_status;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	efct = io->efct;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	/* BLS isn't really a "SCSI" concept, but use SCSI status */
84762306a36Sopenharmony_ci	if (status) {
84862306a36Sopenharmony_ci		io_error_log(io, "s=%#x x=%#x\n", status, ext_status);
84962306a36Sopenharmony_ci		bls_status = EFCT_SCSI_STATUS_ERROR;
85062306a36Sopenharmony_ci	} else {
85162306a36Sopenharmony_ci		bls_status = EFCT_SCSI_STATUS_GOOD;
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	if (io->bls_cb) {
85562306a36Sopenharmony_ci		efct_scsi_io_cb_t bls_cb = io->bls_cb;
85662306a36Sopenharmony_ci		void *bls_cb_arg = io->bls_cb_arg;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		io->bls_cb = NULL;
85962306a36Sopenharmony_ci		io->bls_cb_arg = NULL;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		/* invoke callback */
86262306a36Sopenharmony_ci		bls_cb(io, bls_status, 0, bls_cb_arg);
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	efct_scsi_check_pending(efct);
86662306a36Sopenharmony_ci	return 0;
86762306a36Sopenharmony_ci}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cistatic int
87062306a36Sopenharmony_ciefct_target_send_bls_resp(struct efct_io *io,
87162306a36Sopenharmony_ci			  efct_scsi_io_cb_t cb, void *arg)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	struct efct_node *node = io->node;
87462306a36Sopenharmony_ci	struct sli_bls_params *bls = &io->iparam.bls;
87562306a36Sopenharmony_ci	struct efct *efct = node->efct;
87662306a36Sopenharmony_ci	struct fc_ba_acc *acc;
87762306a36Sopenharmony_ci	int rc;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	/* fill out IO structure with everything needed to send BA_ACC */
88062306a36Sopenharmony_ci	memset(&io->iparam, 0, sizeof(io->iparam));
88162306a36Sopenharmony_ci	bls->ox_id = io->init_task_tag;
88262306a36Sopenharmony_ci	bls->rx_id = io->abort_rx_id;
88362306a36Sopenharmony_ci	bls->vpi = io->node->vpi;
88462306a36Sopenharmony_ci	bls->rpi = io->node->rpi;
88562306a36Sopenharmony_ci	bls->s_id = U32_MAX;
88662306a36Sopenharmony_ci	bls->d_id = io->node->node_fc_id;
88762306a36Sopenharmony_ci	bls->rpi_registered = true;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	acc = (void *)bls->payload;
89062306a36Sopenharmony_ci	acc->ba_ox_id = cpu_to_be16(bls->ox_id);
89162306a36Sopenharmony_ci	acc->ba_rx_id = cpu_to_be16(bls->rx_id);
89262306a36Sopenharmony_ci	acc->ba_high_seq_cnt = cpu_to_be16(U16_MAX);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/* generic io fields have already been populated */
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	/* set type and BLS-specific fields */
89762306a36Sopenharmony_ci	io->io_type = EFCT_IO_TYPE_BLS_RESP;
89862306a36Sopenharmony_ci	io->display_name = "bls_rsp";
89962306a36Sopenharmony_ci	io->hio_type = EFCT_HW_BLS_ACC;
90062306a36Sopenharmony_ci	io->bls_cb = cb;
90162306a36Sopenharmony_ci	io->bls_cb_arg = arg;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	/* dispatch IO */
90462306a36Sopenharmony_ci	rc = efct_hw_bls_send(efct, FC_RCTL_BA_ACC, bls,
90562306a36Sopenharmony_ci			      efct_target_bls_resp_cb, io);
90662306a36Sopenharmony_ci	return rc;
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic int efct_bls_send_rjt_cb(struct efct_hw_io *hio, u32 length, int status,
91062306a36Sopenharmony_ci				u32 ext_status, void *app)
91162306a36Sopenharmony_ci{
91262306a36Sopenharmony_ci	struct efct_io *io = app;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	efct_scsi_io_free(io);
91562306a36Sopenharmony_ci	return 0;
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cistruct efct_io *
91962306a36Sopenharmony_ciefct_bls_send_rjt(struct efct_io *io, struct fc_frame_header *hdr)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	struct efct_node *node = io->node;
92262306a36Sopenharmony_ci	struct sli_bls_params *bls = &io->iparam.bls;
92362306a36Sopenharmony_ci	struct efct *efct = node->efct;
92462306a36Sopenharmony_ci	struct fc_ba_rjt *acc;
92562306a36Sopenharmony_ci	int rc;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	/* fill out BLS Response-specific fields */
92862306a36Sopenharmony_ci	io->io_type = EFCT_IO_TYPE_BLS_RESP;
92962306a36Sopenharmony_ci	io->display_name = "ba_rjt";
93062306a36Sopenharmony_ci	io->hio_type = EFCT_HW_BLS_RJT;
93162306a36Sopenharmony_ci	io->init_task_tag = be16_to_cpu(hdr->fh_ox_id);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	/* fill out iparam fields */
93462306a36Sopenharmony_ci	memset(&io->iparam, 0, sizeof(io->iparam));
93562306a36Sopenharmony_ci	bls->ox_id = be16_to_cpu(hdr->fh_ox_id);
93662306a36Sopenharmony_ci	bls->rx_id = be16_to_cpu(hdr->fh_rx_id);
93762306a36Sopenharmony_ci	bls->vpi = io->node->vpi;
93862306a36Sopenharmony_ci	bls->rpi = io->node->rpi;
93962306a36Sopenharmony_ci	bls->s_id = U32_MAX;
94062306a36Sopenharmony_ci	bls->d_id = io->node->node_fc_id;
94162306a36Sopenharmony_ci	bls->rpi_registered = true;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	acc = (void *)bls->payload;
94462306a36Sopenharmony_ci	acc->br_reason = ELS_RJT_UNAB;
94562306a36Sopenharmony_ci	acc->br_explan = ELS_EXPL_NONE;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	rc = efct_hw_bls_send(efct, FC_RCTL_BA_RJT, bls, efct_bls_send_rjt_cb,
94862306a36Sopenharmony_ci			      io);
94962306a36Sopenharmony_ci	if (rc) {
95062306a36Sopenharmony_ci		efc_log_err(efct, "efct_scsi_io_dispatch() failed: %d\n", rc);
95162306a36Sopenharmony_ci		efct_scsi_io_free(io);
95262306a36Sopenharmony_ci		io = NULL;
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci	return io;
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ciint
95862306a36Sopenharmony_ciefct_scsi_send_tmf_resp(struct efct_io *io,
95962306a36Sopenharmony_ci			enum efct_scsi_tmf_resp rspcode,
96062306a36Sopenharmony_ci			u8 addl_rsp_info[3],
96162306a36Sopenharmony_ci			efct_scsi_io_cb_t cb, void *arg)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	int rc;
96462306a36Sopenharmony_ci	struct {
96562306a36Sopenharmony_ci		struct fcp_resp_with_ext rsp_ext;
96662306a36Sopenharmony_ci		struct fcp_resp_rsp_info info;
96762306a36Sopenharmony_ci	} *fcprsp;
96862306a36Sopenharmony_ci	u8 fcp_rspcode;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	io->wire_len = 0;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	switch (rspcode) {
97362306a36Sopenharmony_ci	case EFCT_SCSI_TMF_FUNCTION_COMPLETE:
97462306a36Sopenharmony_ci		fcp_rspcode = FCP_TMF_CMPL;
97562306a36Sopenharmony_ci		break;
97662306a36Sopenharmony_ci	case EFCT_SCSI_TMF_FUNCTION_SUCCEEDED:
97762306a36Sopenharmony_ci	case EFCT_SCSI_TMF_FUNCTION_IO_NOT_FOUND:
97862306a36Sopenharmony_ci		fcp_rspcode = FCP_TMF_CMPL;
97962306a36Sopenharmony_ci		break;
98062306a36Sopenharmony_ci	case EFCT_SCSI_TMF_FUNCTION_REJECTED:
98162306a36Sopenharmony_ci		fcp_rspcode = FCP_TMF_REJECTED;
98262306a36Sopenharmony_ci		break;
98362306a36Sopenharmony_ci	case EFCT_SCSI_TMF_INCORRECT_LOGICAL_UNIT_NUMBER:
98462306a36Sopenharmony_ci		fcp_rspcode = FCP_TMF_INVALID_LUN;
98562306a36Sopenharmony_ci		break;
98662306a36Sopenharmony_ci	case EFCT_SCSI_TMF_SERVICE_DELIVERY:
98762306a36Sopenharmony_ci		fcp_rspcode = FCP_TMF_FAILED;
98862306a36Sopenharmony_ci		break;
98962306a36Sopenharmony_ci	default:
99062306a36Sopenharmony_ci		fcp_rspcode = FCP_TMF_REJECTED;
99162306a36Sopenharmony_ci		break;
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	io->hio_type = EFCT_HW_IO_TARGET_RSP;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	io->scsi_tgt_cb = cb;
99762306a36Sopenharmony_ci	io->scsi_tgt_cb_arg = arg;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (io->tmf_cmd == EFCT_SCSI_TMF_ABORT_TASK) {
100062306a36Sopenharmony_ci		rc = efct_target_send_bls_resp(io, cb, arg);
100162306a36Sopenharmony_ci		return rc;
100262306a36Sopenharmony_ci	}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	/* populate the FCP TMF response */
100562306a36Sopenharmony_ci	fcprsp = io->rspbuf.virt;
100662306a36Sopenharmony_ci	memset(fcprsp, 0, sizeof(*fcprsp));
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	fcprsp->rsp_ext.resp.fr_flags |= FCP_SNS_LEN_VAL;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (addl_rsp_info) {
101162306a36Sopenharmony_ci		memcpy(fcprsp->info._fr_resvd, addl_rsp_info,
101262306a36Sopenharmony_ci		       sizeof(fcprsp->info._fr_resvd));
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci	fcprsp->info.rsp_code = fcp_rspcode;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	io->wire_len = sizeof(*fcprsp);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	fcprsp->rsp_ext.ext.fr_rsp_len =
101962306a36Sopenharmony_ci			cpu_to_be32(sizeof(struct fcp_resp_rsp_info));
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	io->sgl[0].addr = io->rspbuf.phys;
102262306a36Sopenharmony_ci	io->sgl[0].dif_addr = 0;
102362306a36Sopenharmony_ci	io->sgl[0].len = io->wire_len;
102462306a36Sopenharmony_ci	io->sgl_count = 1;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	memset(&io->iparam, 0, sizeof(io->iparam));
102762306a36Sopenharmony_ci	io->iparam.fcp_tgt.ox_id = io->init_task_tag;
102862306a36Sopenharmony_ci	io->iparam.fcp_tgt.offset = 0;
102962306a36Sopenharmony_ci	io->iparam.fcp_tgt.cs_ctl = io->cs_ctl;
103062306a36Sopenharmony_ci	io->iparam.fcp_tgt.timeout = io->timeout;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	rc = efct_scsi_io_dispatch(io, efct_target_io_cb);
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	return rc;
103562306a36Sopenharmony_ci}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_cistatic int
103862306a36Sopenharmony_ciefct_target_abort_cb(struct efct_hw_io *hio, u32 length, int status,
103962306a36Sopenharmony_ci		     u32 ext_status, void *app)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	struct efct_io *io = app;
104262306a36Sopenharmony_ci	struct efct *efct;
104362306a36Sopenharmony_ci	enum efct_scsi_io_status scsi_status;
104462306a36Sopenharmony_ci	efct_scsi_io_cb_t abort_cb;
104562306a36Sopenharmony_ci	void *abort_cb_arg;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	efct = io->efct;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	if (!io->abort_cb)
105062306a36Sopenharmony_ci		goto done;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	abort_cb = io->abort_cb;
105362306a36Sopenharmony_ci	abort_cb_arg = io->abort_cb_arg;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	io->abort_cb = NULL;
105662306a36Sopenharmony_ci	io->abort_cb_arg = NULL;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	switch (status) {
105962306a36Sopenharmony_ci	case SLI4_FC_WCQE_STATUS_SUCCESS:
106062306a36Sopenharmony_ci		scsi_status = EFCT_SCSI_STATUS_GOOD;
106162306a36Sopenharmony_ci		break;
106262306a36Sopenharmony_ci	case SLI4_FC_WCQE_STATUS_LOCAL_REJECT:
106362306a36Sopenharmony_ci		switch (ext_status) {
106462306a36Sopenharmony_ci		case SLI4_FC_LOCAL_REJECT_NO_XRI:
106562306a36Sopenharmony_ci			scsi_status = EFCT_SCSI_STATUS_NO_IO;
106662306a36Sopenharmony_ci			break;
106762306a36Sopenharmony_ci		case SLI4_FC_LOCAL_REJECT_ABORT_IN_PROGRESS:
106862306a36Sopenharmony_ci			scsi_status = EFCT_SCSI_STATUS_ABORT_IN_PROGRESS;
106962306a36Sopenharmony_ci			break;
107062306a36Sopenharmony_ci		default:
107162306a36Sopenharmony_ci			/*we have seen 0x15 (abort in progress)*/
107262306a36Sopenharmony_ci			scsi_status = EFCT_SCSI_STATUS_ERROR;
107362306a36Sopenharmony_ci			break;
107462306a36Sopenharmony_ci		}
107562306a36Sopenharmony_ci		break;
107662306a36Sopenharmony_ci	case SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE:
107762306a36Sopenharmony_ci		scsi_status = EFCT_SCSI_STATUS_CHECK_RESPONSE;
107862306a36Sopenharmony_ci		break;
107962306a36Sopenharmony_ci	default:
108062306a36Sopenharmony_ci		scsi_status = EFCT_SCSI_STATUS_ERROR;
108162306a36Sopenharmony_ci		break;
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci	/* invoke callback */
108462306a36Sopenharmony_ci	abort_cb(io->io_to_abort, scsi_status, 0, abort_cb_arg);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_cidone:
108762306a36Sopenharmony_ci	/* done with IO to abort,efct_ref_get(): efct_scsi_tgt_abort_io() */
108862306a36Sopenharmony_ci	kref_put(&io->io_to_abort->ref, io->io_to_abort->release);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	efct_io_pool_io_free(efct->xport->io_pool, io);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	efct_scsi_check_pending(efct);
109362306a36Sopenharmony_ci	return 0;
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ciint
109762306a36Sopenharmony_ciefct_scsi_tgt_abort_io(struct efct_io *io, efct_scsi_io_cb_t cb, void *arg)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	struct efct *efct;
110062306a36Sopenharmony_ci	struct efct_xport *xport;
110162306a36Sopenharmony_ci	int rc;
110262306a36Sopenharmony_ci	struct efct_io *abort_io = NULL;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	efct = io->efct;
110562306a36Sopenharmony_ci	xport = efct->xport;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	/* take a reference on IO being aborted */
110862306a36Sopenharmony_ci	if (kref_get_unless_zero(&io->ref) == 0) {
110962306a36Sopenharmony_ci		/* command no longer active */
111062306a36Sopenharmony_ci		scsi_io_printf(io, "command no longer active\n");
111162306a36Sopenharmony_ci		return -EIO;
111262306a36Sopenharmony_ci	}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	/*
111562306a36Sopenharmony_ci	 * allocate a new IO to send the abort request. Use efct_io_alloc()
111662306a36Sopenharmony_ci	 * directly, as we need an IO object that will not fail allocation
111762306a36Sopenharmony_ci	 * due to allocations being disabled (in efct_scsi_io_alloc())
111862306a36Sopenharmony_ci	 */
111962306a36Sopenharmony_ci	abort_io = efct_io_pool_io_alloc(efct->xport->io_pool);
112062306a36Sopenharmony_ci	if (!abort_io) {
112162306a36Sopenharmony_ci		atomic_add_return(1, &xport->io_alloc_failed_count);
112262306a36Sopenharmony_ci		kref_put(&io->ref, io->release);
112362306a36Sopenharmony_ci		return -EIO;
112462306a36Sopenharmony_ci	}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	/* Save the target server callback and argument */
112762306a36Sopenharmony_ci	/* set generic fields */
112862306a36Sopenharmony_ci	abort_io->cmd_tgt = true;
112962306a36Sopenharmony_ci	abort_io->node = io->node;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	/* set type and abort-specific fields */
113262306a36Sopenharmony_ci	abort_io->io_type = EFCT_IO_TYPE_ABORT;
113362306a36Sopenharmony_ci	abort_io->display_name = "tgt_abort";
113462306a36Sopenharmony_ci	abort_io->io_to_abort = io;
113562306a36Sopenharmony_ci	abort_io->send_abts = false;
113662306a36Sopenharmony_ci	abort_io->abort_cb = cb;
113762306a36Sopenharmony_ci	abort_io->abort_cb_arg = arg;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	/* now dispatch IO */
114062306a36Sopenharmony_ci	rc = efct_scsi_io_dispatch_abort(abort_io, efct_target_abort_cb);
114162306a36Sopenharmony_ci	if (rc)
114262306a36Sopenharmony_ci		kref_put(&io->ref, io->release);
114362306a36Sopenharmony_ci	return rc;
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_civoid
114762306a36Sopenharmony_ciefct_scsi_io_complete(struct efct_io *io)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	if (io->io_free) {
115062306a36Sopenharmony_ci		efc_log_debug(io->efct, "completion for non-busy io tag 0x%x\n",
115162306a36Sopenharmony_ci			      io->tag);
115262306a36Sopenharmony_ci		return;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	scsi_io_trace(io, "freeing io 0x%p %s\n", io, io->display_name);
115662306a36Sopenharmony_ci	kref_put(&io->ref, io->release);
115762306a36Sopenharmony_ci}
1158