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_unsol.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define frame_printf(efct, hdr, fmt, ...) \
1162306a36Sopenharmony_ci	do { \
1262306a36Sopenharmony_ci		char s_id_text[16]; \
1362306a36Sopenharmony_ci		efc_node_fcid_display(ntoh24((hdr)->fh_s_id), \
1462306a36Sopenharmony_ci			s_id_text, sizeof(s_id_text)); \
1562306a36Sopenharmony_ci		efc_log_debug(efct, "[%06x.%s] %02x/%04x/%04x: " fmt, \
1662306a36Sopenharmony_ci			ntoh24((hdr)->fh_d_id), s_id_text, \
1762306a36Sopenharmony_ci			(hdr)->fh_r_ctl, be16_to_cpu((hdr)->fh_ox_id), \
1862306a36Sopenharmony_ci			be16_to_cpu((hdr)->fh_rx_id), ##__VA_ARGS__); \
1962306a36Sopenharmony_ci	} while (0)
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic struct efct_node *
2262306a36Sopenharmony_ciefct_node_find(struct efct *efct, u32 port_id, u32 node_id)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct efct_node *node;
2562306a36Sopenharmony_ci	u64 id = (u64)port_id << 32 | node_id;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	/*
2862306a36Sopenharmony_ci	 * During node shutdown, Lookup will be removed first,
2962306a36Sopenharmony_ci	 * before announcing to backend. So, no new IOs will be allowed
3062306a36Sopenharmony_ci	 */
3162306a36Sopenharmony_ci	/* Find a target node, given s_id and d_id */
3262306a36Sopenharmony_ci	node = xa_load(&efct->lookup, id);
3362306a36Sopenharmony_ci	if (node)
3462306a36Sopenharmony_ci		kref_get(&node->ref);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	return node;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int
4062306a36Sopenharmony_ciefct_dispatch_frame(struct efct *efct, struct efc_hw_sequence *seq)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct efct_node *node;
4362306a36Sopenharmony_ci	struct fc_frame_header *hdr;
4462306a36Sopenharmony_ci	u32 s_id, d_id;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	hdr = seq->header->dma.virt;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* extract the s_id and d_id */
4962306a36Sopenharmony_ci	s_id = ntoh24(hdr->fh_s_id);
5062306a36Sopenharmony_ci	d_id = ntoh24(hdr->fh_d_id);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (!(hdr->fh_type == FC_TYPE_FCP || hdr->fh_type == FC_TYPE_BLS))
5362306a36Sopenharmony_ci		return -EIO;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (hdr->fh_type == FC_TYPE_FCP) {
5662306a36Sopenharmony_ci		node = efct_node_find(efct, d_id, s_id);
5762306a36Sopenharmony_ci		if (!node) {
5862306a36Sopenharmony_ci			efc_log_err(efct,
5962306a36Sopenharmony_ci				    "Node not found, drop cmd d_id:%x s_id:%x\n",
6062306a36Sopenharmony_ci				    d_id, s_id);
6162306a36Sopenharmony_ci			efct_hw_sequence_free(&efct->hw, seq);
6262306a36Sopenharmony_ci			return 0;
6362306a36Sopenharmony_ci		}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		efct_dispatch_fcp_cmd(node, seq);
6662306a36Sopenharmony_ci	} else {
6762306a36Sopenharmony_ci		node = efct_node_find(efct, d_id, s_id);
6862306a36Sopenharmony_ci		if (!node) {
6962306a36Sopenharmony_ci			efc_log_err(efct, "ABTS: Node not found, d_id:%x s_id:%x\n",
7062306a36Sopenharmony_ci				    d_id, s_id);
7162306a36Sopenharmony_ci			return -EIO;
7262306a36Sopenharmony_ci		}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		efc_log_err(efct, "Received ABTS for Node:%p\n", node);
7562306a36Sopenharmony_ci		efct_node_recv_abts_frame(node, seq);
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	kref_put(&node->ref, node->release);
7962306a36Sopenharmony_ci	efct_hw_sequence_free(&efct->hw, seq);
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciint
8462306a36Sopenharmony_ciefct_unsolicited_cb(void *arg, struct efc_hw_sequence *seq)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct efct *efct = arg;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Process FCP command */
8962306a36Sopenharmony_ci	if (!efct_dispatch_frame(efct, seq))
9062306a36Sopenharmony_ci		return 0;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* Forward frame to discovery lib */
9362306a36Sopenharmony_ci	efc_dispatch_frame(efct->efcport, seq);
9462306a36Sopenharmony_ci	return 0;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int
9862306a36Sopenharmony_ciefct_fc_tmf_rejected_cb(struct efct_io *io,
9962306a36Sopenharmony_ci			enum efct_scsi_io_status scsi_status,
10062306a36Sopenharmony_ci			u32 flags, void *arg)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	efct_scsi_io_free(io);
10362306a36Sopenharmony_ci	return 0;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void
10762306a36Sopenharmony_ciefct_dispatch_unsol_tmf(struct efct_io *io, u8 tm_flags, u32 lun)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	u32 i;
11062306a36Sopenharmony_ci	struct {
11162306a36Sopenharmony_ci		u32 mask;
11262306a36Sopenharmony_ci		enum efct_scsi_tmf_cmd cmd;
11362306a36Sopenharmony_ci	} tmflist[] = {
11462306a36Sopenharmony_ci	{FCP_TMF_ABT_TASK_SET, EFCT_SCSI_TMF_ABORT_TASK_SET},
11562306a36Sopenharmony_ci	{FCP_TMF_CLR_TASK_SET, EFCT_SCSI_TMF_CLEAR_TASK_SET},
11662306a36Sopenharmony_ci	{FCP_TMF_LUN_RESET, EFCT_SCSI_TMF_LOGICAL_UNIT_RESET},
11762306a36Sopenharmony_ci	{FCP_TMF_TGT_RESET, EFCT_SCSI_TMF_TARGET_RESET},
11862306a36Sopenharmony_ci	{FCP_TMF_CLR_ACA, EFCT_SCSI_TMF_CLEAR_ACA} };
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	io->exp_xfer_len = 0;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tmflist); i++) {
12362306a36Sopenharmony_ci		if (tmflist[i].mask & tm_flags) {
12462306a36Sopenharmony_ci			io->tmf_cmd = tmflist[i].cmd;
12562306a36Sopenharmony_ci			efct_scsi_recv_tmf(io, lun, tmflist[i].cmd, NULL, 0);
12662306a36Sopenharmony_ci			break;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	if (i == ARRAY_SIZE(tmflist)) {
13062306a36Sopenharmony_ci		/* Not handled */
13162306a36Sopenharmony_ci		efc_log_err(io->node->efct, "TMF x%x rejected\n", tm_flags);
13262306a36Sopenharmony_ci		efct_scsi_send_tmf_resp(io, EFCT_SCSI_TMF_FUNCTION_REJECTED,
13362306a36Sopenharmony_ci					NULL, efct_fc_tmf_rejected_cb, NULL);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int
13862306a36Sopenharmony_ciefct_validate_fcp_cmd(struct efct *efct, struct efc_hw_sequence *seq)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	/*
14162306a36Sopenharmony_ci	 * If we received less than FCP_CMND_IU bytes, assume that the frame is
14262306a36Sopenharmony_ci	 * corrupted in some way and drop it.
14362306a36Sopenharmony_ci	 * This was seen when jamming the FCTL
14462306a36Sopenharmony_ci	 * fill bytes field.
14562306a36Sopenharmony_ci	 */
14662306a36Sopenharmony_ci	if (seq->payload->dma.len < sizeof(struct fcp_cmnd)) {
14762306a36Sopenharmony_ci		struct fc_frame_header	*fchdr = seq->header->dma.virt;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		efc_log_debug(efct,
15062306a36Sopenharmony_ci			      "drop ox_id %04x payload (%zd) less than (%zd)\n",
15162306a36Sopenharmony_ci			      be16_to_cpu(fchdr->fh_ox_id),
15262306a36Sopenharmony_ci			      seq->payload->dma.len, sizeof(struct fcp_cmnd));
15362306a36Sopenharmony_ci		return -EIO;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci	return 0;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic void
15962306a36Sopenharmony_ciefct_populate_io_fcp_cmd(struct efct_io *io, struct fcp_cmnd *cmnd,
16062306a36Sopenharmony_ci			 struct fc_frame_header *fchdr, bool sit)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	io->init_task_tag = be16_to_cpu(fchdr->fh_ox_id);
16362306a36Sopenharmony_ci	/* note, tgt_task_tag, hw_tag  set when HW io is allocated */
16462306a36Sopenharmony_ci	io->exp_xfer_len = be32_to_cpu(cmnd->fc_dl);
16562306a36Sopenharmony_ci	io->transferred = 0;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* The upper 7 bits of CS_CTL is the frame priority thru the SAN.
16862306a36Sopenharmony_ci	 * Our assertion here is, the priority given to a frame containing
16962306a36Sopenharmony_ci	 * the FCP cmd should be the priority given to ALL frames contained
17062306a36Sopenharmony_ci	 * in that IO. Thus we need to save the incoming CS_CTL here.
17162306a36Sopenharmony_ci	 */
17262306a36Sopenharmony_ci	if (ntoh24(fchdr->fh_f_ctl) & FC_FC_RES_B17)
17362306a36Sopenharmony_ci		io->cs_ctl = fchdr->fh_cs_ctl;
17462306a36Sopenharmony_ci	else
17562306a36Sopenharmony_ci		io->cs_ctl = 0;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	io->seq_init = sit;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic u32
18162306a36Sopenharmony_ciefct_get_flags_fcp_cmd(struct fcp_cmnd *cmnd)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	u32 flags = 0;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	switch (cmnd->fc_pri_ta & FCP_PTA_MASK) {
18662306a36Sopenharmony_ci	case FCP_PTA_SIMPLE:
18762306a36Sopenharmony_ci		flags |= EFCT_SCSI_CMD_SIMPLE;
18862306a36Sopenharmony_ci		break;
18962306a36Sopenharmony_ci	case FCP_PTA_HEADQ:
19062306a36Sopenharmony_ci		flags |= EFCT_SCSI_CMD_HEAD_OF_QUEUE;
19162306a36Sopenharmony_ci		break;
19262306a36Sopenharmony_ci	case FCP_PTA_ORDERED:
19362306a36Sopenharmony_ci		flags |= EFCT_SCSI_CMD_ORDERED;
19462306a36Sopenharmony_ci		break;
19562306a36Sopenharmony_ci	case FCP_PTA_ACA:
19662306a36Sopenharmony_ci		flags |= EFCT_SCSI_CMD_ACA;
19762306a36Sopenharmony_ci		break;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	if (cmnd->fc_flags & FCP_CFL_WRDATA)
20062306a36Sopenharmony_ci		flags |= EFCT_SCSI_CMD_DIR_IN;
20162306a36Sopenharmony_ci	if (cmnd->fc_flags & FCP_CFL_RDDATA)
20262306a36Sopenharmony_ci		flags |= EFCT_SCSI_CMD_DIR_OUT;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return flags;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic void
20862306a36Sopenharmony_ciefct_sframe_common_send_cb(void *arg, u8 *cqe, int status)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct efct_hw_send_frame_context *ctx = arg;
21162306a36Sopenharmony_ci	struct efct_hw *hw = ctx->hw;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* Free WQ completion callback */
21462306a36Sopenharmony_ci	efct_hw_reqtag_free(hw, ctx->wqcb);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* Free sequence */
21762306a36Sopenharmony_ci	efct_hw_sequence_free(hw, ctx->seq);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic int
22162306a36Sopenharmony_ciefct_sframe_common_send(struct efct_node *node,
22262306a36Sopenharmony_ci			struct efc_hw_sequence *seq,
22362306a36Sopenharmony_ci			enum fc_rctl r_ctl, u32 f_ctl,
22462306a36Sopenharmony_ci			u8 type, void *payload, u32 payload_len)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct efct *efct = node->efct;
22762306a36Sopenharmony_ci	struct efct_hw *hw = &efct->hw;
22862306a36Sopenharmony_ci	int rc = 0;
22962306a36Sopenharmony_ci	struct fc_frame_header *req_hdr = seq->header->dma.virt;
23062306a36Sopenharmony_ci	struct fc_frame_header hdr;
23162306a36Sopenharmony_ci	struct efct_hw_send_frame_context *ctx;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	u32 heap_size = seq->payload->dma.size;
23462306a36Sopenharmony_ci	uintptr_t heap_phys_base = seq->payload->dma.phys;
23562306a36Sopenharmony_ci	u8 *heap_virt_base = seq->payload->dma.virt;
23662306a36Sopenharmony_ci	u32 heap_offset = 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* Build the FC header reusing the RQ header DMA buffer */
23962306a36Sopenharmony_ci	memset(&hdr, 0, sizeof(hdr));
24062306a36Sopenharmony_ci	hdr.fh_r_ctl = r_ctl;
24162306a36Sopenharmony_ci	/* send it back to whomever sent it to us */
24262306a36Sopenharmony_ci	memcpy(hdr.fh_d_id, req_hdr->fh_s_id, sizeof(hdr.fh_d_id));
24362306a36Sopenharmony_ci	memcpy(hdr.fh_s_id, req_hdr->fh_d_id, sizeof(hdr.fh_s_id));
24462306a36Sopenharmony_ci	hdr.fh_type = type;
24562306a36Sopenharmony_ci	hton24(hdr.fh_f_ctl, f_ctl);
24662306a36Sopenharmony_ci	hdr.fh_ox_id = req_hdr->fh_ox_id;
24762306a36Sopenharmony_ci	hdr.fh_rx_id = req_hdr->fh_rx_id;
24862306a36Sopenharmony_ci	hdr.fh_cs_ctl = 0;
24962306a36Sopenharmony_ci	hdr.fh_df_ctl = 0;
25062306a36Sopenharmony_ci	hdr.fh_seq_cnt = 0;
25162306a36Sopenharmony_ci	hdr.fh_parm_offset = 0;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/*
25462306a36Sopenharmony_ci	 * send_frame_seq_id is an atomic, we just let it increment,
25562306a36Sopenharmony_ci	 * while storing only the low 8 bits to hdr->seq_id
25662306a36Sopenharmony_ci	 */
25762306a36Sopenharmony_ci	hdr.fh_seq_id = (u8)atomic_add_return(1, &hw->send_frame_seq_id);
25862306a36Sopenharmony_ci	hdr.fh_seq_id--;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* Allocate and fill in the send frame request context */
26162306a36Sopenharmony_ci	ctx = (void *)(heap_virt_base + heap_offset);
26262306a36Sopenharmony_ci	heap_offset += sizeof(*ctx);
26362306a36Sopenharmony_ci	if (heap_offset > heap_size) {
26462306a36Sopenharmony_ci		efc_log_err(efct, "Fill send frame failed offset %d size %d\n",
26562306a36Sopenharmony_ci			    heap_offset, heap_size);
26662306a36Sopenharmony_ci		return -EIO;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	memset(ctx, 0, sizeof(*ctx));
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* Save sequence */
27262306a36Sopenharmony_ci	ctx->seq = seq;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* Allocate a response payload DMA buffer from the heap */
27562306a36Sopenharmony_ci	ctx->payload.phys = heap_phys_base + heap_offset;
27662306a36Sopenharmony_ci	ctx->payload.virt = heap_virt_base + heap_offset;
27762306a36Sopenharmony_ci	ctx->payload.size = payload_len;
27862306a36Sopenharmony_ci	ctx->payload.len = payload_len;
27962306a36Sopenharmony_ci	heap_offset += payload_len;
28062306a36Sopenharmony_ci	if (heap_offset > heap_size) {
28162306a36Sopenharmony_ci		efc_log_err(efct, "Fill send frame failed offset %d size %d\n",
28262306a36Sopenharmony_ci			    heap_offset, heap_size);
28362306a36Sopenharmony_ci		return -EIO;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* Copy the payload in */
28762306a36Sopenharmony_ci	memcpy(ctx->payload.virt, payload, payload_len);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* Send */
29062306a36Sopenharmony_ci	rc = efct_hw_send_frame(&efct->hw, (void *)&hdr, FC_SOF_N3,
29162306a36Sopenharmony_ci				FC_EOF_T, &ctx->payload, ctx,
29262306a36Sopenharmony_ci				efct_sframe_common_send_cb, ctx);
29362306a36Sopenharmony_ci	if (rc)
29462306a36Sopenharmony_ci		efc_log_debug(efct, "efct_hw_send_frame failed: %d\n", rc);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return rc;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic int
30062306a36Sopenharmony_ciefct_sframe_send_fcp_rsp(struct efct_node *node, struct efc_hw_sequence *seq,
30162306a36Sopenharmony_ci			 void *rsp, u32 rsp_len)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	return efct_sframe_common_send(node, seq, FC_RCTL_DD_CMD_STATUS,
30462306a36Sopenharmony_ci				      FC_FC_EX_CTX |
30562306a36Sopenharmony_ci				      FC_FC_LAST_SEQ |
30662306a36Sopenharmony_ci				      FC_FC_END_SEQ |
30762306a36Sopenharmony_ci				      FC_FC_SEQ_INIT,
30862306a36Sopenharmony_ci				      FC_TYPE_FCP,
30962306a36Sopenharmony_ci				      rsp, rsp_len);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic int
31362306a36Sopenharmony_ciefct_sframe_send_task_set_full_or_busy(struct efct_node *node,
31462306a36Sopenharmony_ci				       struct efc_hw_sequence *seq)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct fcp_resp_with_ext fcprsp;
31762306a36Sopenharmony_ci	struct fcp_cmnd *fcpcmd = seq->payload->dma.virt;
31862306a36Sopenharmony_ci	int rc = 0;
31962306a36Sopenharmony_ci	unsigned long flags = 0;
32062306a36Sopenharmony_ci	struct efct *efct = node->efct;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* construct task set full or busy response */
32362306a36Sopenharmony_ci	memset(&fcprsp, 0, sizeof(fcprsp));
32462306a36Sopenharmony_ci	spin_lock_irqsave(&node->active_ios_lock, flags);
32562306a36Sopenharmony_ci	fcprsp.resp.fr_status = list_empty(&node->active_ios) ?
32662306a36Sopenharmony_ci				SAM_STAT_BUSY : SAM_STAT_TASK_SET_FULL;
32762306a36Sopenharmony_ci	spin_unlock_irqrestore(&node->active_ios_lock, flags);
32862306a36Sopenharmony_ci	*((u32 *)&fcprsp.ext.fr_resid) = be32_to_cpu(fcpcmd->fc_dl);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* send it using send_frame */
33162306a36Sopenharmony_ci	rc = efct_sframe_send_fcp_rsp(node, seq, &fcprsp, sizeof(fcprsp));
33262306a36Sopenharmony_ci	if (rc)
33362306a36Sopenharmony_ci		efc_log_debug(efct, "efct_sframe_send_fcp_rsp failed %d\n", rc);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return rc;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ciint
33962306a36Sopenharmony_ciefct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct efct *efct = node->efct;
34262306a36Sopenharmony_ci	struct fc_frame_header *fchdr = seq->header->dma.virt;
34362306a36Sopenharmony_ci	struct fcp_cmnd	*cmnd = NULL;
34462306a36Sopenharmony_ci	struct efct_io *io = NULL;
34562306a36Sopenharmony_ci	u32 lun;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (!seq->payload) {
34862306a36Sopenharmony_ci		efc_log_err(efct, "Sequence payload is NULL.\n");
34962306a36Sopenharmony_ci		return -EIO;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	cmnd = seq->payload->dma.virt;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* perform FCP_CMND validation check(s) */
35562306a36Sopenharmony_ci	if (efct_validate_fcp_cmd(efct, seq))
35662306a36Sopenharmony_ci		return -EIO;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	lun = scsilun_to_int(&cmnd->fc_lun);
35962306a36Sopenharmony_ci	if (lun == U32_MAX)
36062306a36Sopenharmony_ci		return -EIO;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	io = efct_scsi_io_alloc(node);
36362306a36Sopenharmony_ci	if (!io) {
36462306a36Sopenharmony_ci		int rc;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		/* Use SEND_FRAME to send task set full or busy */
36762306a36Sopenharmony_ci		rc = efct_sframe_send_task_set_full_or_busy(node, seq);
36862306a36Sopenharmony_ci		if (rc)
36962306a36Sopenharmony_ci			efc_log_err(efct, "Failed to send busy task: %d\n", rc);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		return rc;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	io->hw_priv = seq->hw_priv;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	io->app_id = 0;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* RQ pair, if we got here, SIT=1 */
37962306a36Sopenharmony_ci	efct_populate_io_fcp_cmd(io, cmnd, fchdr, true);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (cmnd->fc_tm_flags) {
38262306a36Sopenharmony_ci		efct_dispatch_unsol_tmf(io, cmnd->fc_tm_flags, lun);
38362306a36Sopenharmony_ci	} else {
38462306a36Sopenharmony_ci		u32 flags = efct_get_flags_fcp_cmd(cmnd);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		if (cmnd->fc_flags & FCP_CFL_LEN_MASK) {
38762306a36Sopenharmony_ci			efc_log_err(efct, "Additional CDB not supported\n");
38862306a36Sopenharmony_ci			return -EIO;
38962306a36Sopenharmony_ci		}
39062306a36Sopenharmony_ci		/*
39162306a36Sopenharmony_ci		 * Can return failure for things like task set full and UAs,
39262306a36Sopenharmony_ci		 * no need to treat as a dropped frame if rc != 0
39362306a36Sopenharmony_ci		 */
39462306a36Sopenharmony_ci		efct_scsi_recv_cmd(io, lun, cmnd->fc_cdb,
39562306a36Sopenharmony_ci				   sizeof(cmnd->fc_cdb), flags);
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return 0;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic int
40262306a36Sopenharmony_ciefct_process_abts(struct efct_io *io, struct fc_frame_header *hdr)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct efct_node *node = io->node;
40562306a36Sopenharmony_ci	struct efct *efct = io->efct;
40662306a36Sopenharmony_ci	u16 ox_id = be16_to_cpu(hdr->fh_ox_id);
40762306a36Sopenharmony_ci	u16 rx_id = be16_to_cpu(hdr->fh_rx_id);
40862306a36Sopenharmony_ci	struct efct_io *abortio;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* Find IO and attempt to take a reference on it */
41162306a36Sopenharmony_ci	abortio = efct_io_find_tgt_io(efct, node, ox_id, rx_id);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (abortio) {
41462306a36Sopenharmony_ci		/* Got a reference on the IO. Hold it until backend
41562306a36Sopenharmony_ci		 * is notified below
41662306a36Sopenharmony_ci		 */
41762306a36Sopenharmony_ci		efc_log_info(node->efct, "Abort ox_id [%04x] rx_id [%04x]\n",
41862306a36Sopenharmony_ci			     ox_id, rx_id);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci		/*
42162306a36Sopenharmony_ci		 * Save the ox_id for the ABTS as the init_task_tag in our
42262306a36Sopenharmony_ci		 * manufactured
42362306a36Sopenharmony_ci		 * TMF IO object
42462306a36Sopenharmony_ci		 */
42562306a36Sopenharmony_ci		io->display_name = "abts";
42662306a36Sopenharmony_ci		io->init_task_tag = ox_id;
42762306a36Sopenharmony_ci		/* don't set tgt_task_tag, don't want to confuse with XRI */
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		/*
43062306a36Sopenharmony_ci		 * Save the rx_id from the ABTS as it is
43162306a36Sopenharmony_ci		 * needed for the BLS response,
43262306a36Sopenharmony_ci		 * regardless of the IO context's rx_id
43362306a36Sopenharmony_ci		 */
43462306a36Sopenharmony_ci		io->abort_rx_id = rx_id;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		/* Call target server command abort */
43762306a36Sopenharmony_ci		io->tmf_cmd = EFCT_SCSI_TMF_ABORT_TASK;
43862306a36Sopenharmony_ci		efct_scsi_recv_tmf(io, abortio->tgt_io.lun,
43962306a36Sopenharmony_ci				   EFCT_SCSI_TMF_ABORT_TASK, abortio, 0);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		/*
44262306a36Sopenharmony_ci		 * Backend will have taken an additional
44362306a36Sopenharmony_ci		 * reference on the IO if needed;
44462306a36Sopenharmony_ci		 * done with current reference.
44562306a36Sopenharmony_ci		 */
44662306a36Sopenharmony_ci		kref_put(&abortio->ref, abortio->release);
44762306a36Sopenharmony_ci	} else {
44862306a36Sopenharmony_ci		/*
44962306a36Sopenharmony_ci		 * Either IO was not found or it has been
45062306a36Sopenharmony_ci		 * freed between finding it
45162306a36Sopenharmony_ci		 * and attempting to get the reference,
45262306a36Sopenharmony_ci		 */
45362306a36Sopenharmony_ci		efc_log_info(node->efct, "Abort: ox_id [%04x], IO not found\n",
45462306a36Sopenharmony_ci			     ox_id);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		/* Send a BA_RJT */
45762306a36Sopenharmony_ci		efct_bls_send_rjt(io, hdr);
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci	return 0;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ciint
46362306a36Sopenharmony_ciefct_node_recv_abts_frame(struct efct_node *node, struct efc_hw_sequence *seq)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct efct *efct = node->efct;
46662306a36Sopenharmony_ci	struct fc_frame_header *hdr = seq->header->dma.virt;
46762306a36Sopenharmony_ci	struct efct_io *io = NULL;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	node->abort_cnt++;
47062306a36Sopenharmony_ci	io = efct_scsi_io_alloc(node);
47162306a36Sopenharmony_ci	if (io) {
47262306a36Sopenharmony_ci		io->hw_priv = seq->hw_priv;
47362306a36Sopenharmony_ci		/* If we got this far, SIT=1 */
47462306a36Sopenharmony_ci		io->seq_init = 1;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		/* fill out generic fields */
47762306a36Sopenharmony_ci		io->efct = efct;
47862306a36Sopenharmony_ci		io->node = node;
47962306a36Sopenharmony_ci		io->cmd_tgt = true;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		efct_process_abts(io, seq->header->dma.virt);
48262306a36Sopenharmony_ci	} else {
48362306a36Sopenharmony_ci		efc_log_err(efct,
48462306a36Sopenharmony_ci			    "SCSI IO allocation failed for ABTS received ");
48562306a36Sopenharmony_ci		efc_log_err(efct, "s_id %06x d_id %06x ox_id %04x rx_id %04x\n",
48662306a36Sopenharmony_ci			    ntoh24(hdr->fh_s_id), ntoh24(hdr->fh_d_id),
48762306a36Sopenharmony_ci			    be16_to_cpu(hdr->fh_ox_id),
48862306a36Sopenharmony_ci			    be16_to_cpu(hdr->fh_rx_id));
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	return 0;
49262306a36Sopenharmony_ci}
493