162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/* Authors: Bernard Metzler <bmt@zurich.ibm.com> */
462306a36Sopenharmony_ci/* Copyright (c) 2008-2019, IBM Corporation */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/errno.h>
762306a36Sopenharmony_ci#include <linux/types.h>
862306a36Sopenharmony_ci#include <linux/net.h>
962306a36Sopenharmony_ci#include <linux/scatterlist.h>
1062306a36Sopenharmony_ci#include <linux/highmem.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <rdma/iw_cm.h>
1362306a36Sopenharmony_ci#include <rdma/ib_verbs.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "siw.h"
1662306a36Sopenharmony_ci#include "siw_verbs.h"
1762306a36Sopenharmony_ci#include "siw_mem.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * siw_rx_umem()
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * Receive data of @len into target referenced by @dest_addr.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * @srx:	Receive Context
2562306a36Sopenharmony_ci * @umem:	siw representation of target memory
2662306a36Sopenharmony_ci * @dest_addr:	user virtual address
2762306a36Sopenharmony_ci * @len:	number of bytes to place
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cistatic int siw_rx_umem(struct siw_rx_stream *srx, struct siw_umem *umem,
3062306a36Sopenharmony_ci		       u64 dest_addr, int len)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	int copied = 0;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	while (len) {
3562306a36Sopenharmony_ci		struct page *p;
3662306a36Sopenharmony_ci		int pg_off, bytes, rv;
3762306a36Sopenharmony_ci		void *dest;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci		p = siw_get_upage(umem, dest_addr);
4062306a36Sopenharmony_ci		if (unlikely(!p)) {
4162306a36Sopenharmony_ci			pr_warn("siw: %s: [QP %u]: bogus addr: %pK, %pK\n",
4262306a36Sopenharmony_ci				__func__, qp_id(rx_qp(srx)),
4362306a36Sopenharmony_ci				(void *)(uintptr_t)dest_addr,
4462306a36Sopenharmony_ci				(void *)(uintptr_t)umem->fp_addr);
4562306a36Sopenharmony_ci			/* siw internal error */
4662306a36Sopenharmony_ci			srx->skb_copied += copied;
4762306a36Sopenharmony_ci			srx->skb_new -= copied;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci			return -EFAULT;
5062306a36Sopenharmony_ci		}
5162306a36Sopenharmony_ci		pg_off = dest_addr & ~PAGE_MASK;
5262306a36Sopenharmony_ci		bytes = min(len, (int)PAGE_SIZE - pg_off);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		siw_dbg_qp(rx_qp(srx), "page %pK, bytes=%u\n", p, bytes);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		dest = kmap_atomic(p);
5762306a36Sopenharmony_ci		rv = skb_copy_bits(srx->skb, srx->skb_offset, dest + pg_off,
5862306a36Sopenharmony_ci				   bytes);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		if (unlikely(rv)) {
6162306a36Sopenharmony_ci			kunmap_atomic(dest);
6262306a36Sopenharmony_ci			srx->skb_copied += copied;
6362306a36Sopenharmony_ci			srx->skb_new -= copied;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci			pr_warn("siw: [QP %u]: %s, len %d, page %p, rv %d\n",
6662306a36Sopenharmony_ci				qp_id(rx_qp(srx)), __func__, len, p, rv);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci			return -EFAULT;
6962306a36Sopenharmony_ci		}
7062306a36Sopenharmony_ci		if (srx->mpa_crc_hd) {
7162306a36Sopenharmony_ci			if (rdma_is_kernel_res(&rx_qp(srx)->base_qp.res)) {
7262306a36Sopenharmony_ci				crypto_shash_update(srx->mpa_crc_hd,
7362306a36Sopenharmony_ci					(u8 *)(dest + pg_off), bytes);
7462306a36Sopenharmony_ci				kunmap_atomic(dest);
7562306a36Sopenharmony_ci			} else {
7662306a36Sopenharmony_ci				kunmap_atomic(dest);
7762306a36Sopenharmony_ci				/*
7862306a36Sopenharmony_ci				 * Do CRC on original, not target buffer.
7962306a36Sopenharmony_ci				 * Some user land applications may
8062306a36Sopenharmony_ci				 * concurrently write the target buffer,
8162306a36Sopenharmony_ci				 * which would yield a broken CRC.
8262306a36Sopenharmony_ci				 * Walking the skb twice is very ineffcient.
8362306a36Sopenharmony_ci				 * Folding the CRC into skb_copy_bits()
8462306a36Sopenharmony_ci				 * would be much better, but is currently
8562306a36Sopenharmony_ci				 * not supported.
8662306a36Sopenharmony_ci				 */
8762306a36Sopenharmony_ci				siw_crc_skb(srx, bytes);
8862306a36Sopenharmony_ci			}
8962306a36Sopenharmony_ci		} else {
9062306a36Sopenharmony_ci			kunmap_atomic(dest);
9162306a36Sopenharmony_ci		}
9262306a36Sopenharmony_ci		srx->skb_offset += bytes;
9362306a36Sopenharmony_ci		copied += bytes;
9462306a36Sopenharmony_ci		len -= bytes;
9562306a36Sopenharmony_ci		dest_addr += bytes;
9662306a36Sopenharmony_ci		pg_off = 0;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci	srx->skb_copied += copied;
9962306a36Sopenharmony_ci	srx->skb_new -= copied;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return copied;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int siw_rx_kva(struct siw_rx_stream *srx, void *kva, int len)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int rv;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	siw_dbg_qp(rx_qp(srx), "kva: 0x%pK, len: %u\n", kva, len);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	rv = skb_copy_bits(srx->skb, srx->skb_offset, kva, len);
11162306a36Sopenharmony_ci	if (unlikely(rv)) {
11262306a36Sopenharmony_ci		pr_warn("siw: [QP %u]: %s, len %d, kva 0x%pK, rv %d\n",
11362306a36Sopenharmony_ci			qp_id(rx_qp(srx)), __func__, len, kva, rv);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		return rv;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci	if (srx->mpa_crc_hd)
11862306a36Sopenharmony_ci		crypto_shash_update(srx->mpa_crc_hd, (u8 *)kva, len);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	srx->skb_offset += len;
12162306a36Sopenharmony_ci	srx->skb_copied += len;
12262306a36Sopenharmony_ci	srx->skb_new -= len;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return len;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic int siw_rx_pbl(struct siw_rx_stream *srx, int *pbl_idx,
12862306a36Sopenharmony_ci		      struct siw_mem *mem, u64 addr, int len)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct siw_pbl *pbl = mem->pbl;
13162306a36Sopenharmony_ci	u64 offset = addr - mem->va;
13262306a36Sopenharmony_ci	int copied = 0;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	while (len) {
13562306a36Sopenharmony_ci		int bytes;
13662306a36Sopenharmony_ci		dma_addr_t buf_addr =
13762306a36Sopenharmony_ci			siw_pbl_get_buffer(pbl, offset, &bytes, pbl_idx);
13862306a36Sopenharmony_ci		if (!buf_addr)
13962306a36Sopenharmony_ci			break;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		bytes = min(bytes, len);
14262306a36Sopenharmony_ci		if (siw_rx_kva(srx, ib_virt_dma_to_ptr(buf_addr), bytes) ==
14362306a36Sopenharmony_ci		    bytes) {
14462306a36Sopenharmony_ci			copied += bytes;
14562306a36Sopenharmony_ci			offset += bytes;
14662306a36Sopenharmony_ci			len -= bytes;
14762306a36Sopenharmony_ci		} else {
14862306a36Sopenharmony_ci			break;
14962306a36Sopenharmony_ci		}
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci	return copied;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/*
15562306a36Sopenharmony_ci * siw_rresp_check_ntoh()
15662306a36Sopenharmony_ci *
15762306a36Sopenharmony_ci * Check incoming RRESP fragment header against expected
15862306a36Sopenharmony_ci * header values and update expected values for potential next
15962306a36Sopenharmony_ci * fragment.
16062306a36Sopenharmony_ci *
16162306a36Sopenharmony_ci * NOTE: This function must be called only if a RRESP DDP segment
16262306a36Sopenharmony_ci *       starts but not for fragmented consecutive pieces of an
16362306a36Sopenharmony_ci *       already started DDP segment.
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_cistatic int siw_rresp_check_ntoh(struct siw_rx_stream *srx,
16662306a36Sopenharmony_ci				struct siw_rx_fpdu *frx)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct iwarp_rdma_rresp *rresp = &srx->hdr.rresp;
16962306a36Sopenharmony_ci	struct siw_wqe *wqe = &frx->wqe_active;
17062306a36Sopenharmony_ci	enum ddp_ecode ecode;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	u32 sink_stag = be32_to_cpu(rresp->sink_stag);
17362306a36Sopenharmony_ci	u64 sink_to = be64_to_cpu(rresp->sink_to);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (frx->first_ddp_seg) {
17662306a36Sopenharmony_ci		srx->ddp_stag = wqe->sqe.sge[0].lkey;
17762306a36Sopenharmony_ci		srx->ddp_to = wqe->sqe.sge[0].laddr;
17862306a36Sopenharmony_ci		frx->pbl_idx = 0;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci	/* Below checks extend beyond the semantics of DDP, and
18162306a36Sopenharmony_ci	 * into RDMAP:
18262306a36Sopenharmony_ci	 * We check if the read response matches exactly the
18362306a36Sopenharmony_ci	 * read request which was send to the remote peer to
18462306a36Sopenharmony_ci	 * trigger this read response. RFC5040/5041 do not
18562306a36Sopenharmony_ci	 * always have a proper error code for the detected
18662306a36Sopenharmony_ci	 * error cases. We choose 'base or bounds error' for
18762306a36Sopenharmony_ci	 * cases where the inbound STag is valid, but offset
18862306a36Sopenharmony_ci	 * or length do not match our response receive state.
18962306a36Sopenharmony_ci	 */
19062306a36Sopenharmony_ci	if (unlikely(srx->ddp_stag != sink_stag)) {
19162306a36Sopenharmony_ci		pr_warn("siw: [QP %u]: rresp stag: %08x != %08x\n",
19262306a36Sopenharmony_ci			qp_id(rx_qp(srx)), sink_stag, srx->ddp_stag);
19362306a36Sopenharmony_ci		ecode = DDP_ECODE_T_INVALID_STAG;
19462306a36Sopenharmony_ci		goto error;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci	if (unlikely(srx->ddp_to != sink_to)) {
19762306a36Sopenharmony_ci		pr_warn("siw: [QP %u]: rresp off: %016llx != %016llx\n",
19862306a36Sopenharmony_ci			qp_id(rx_qp(srx)), (unsigned long long)sink_to,
19962306a36Sopenharmony_ci			(unsigned long long)srx->ddp_to);
20062306a36Sopenharmony_ci		ecode = DDP_ECODE_T_BASE_BOUNDS;
20162306a36Sopenharmony_ci		goto error;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci	if (unlikely(!frx->more_ddp_segs &&
20462306a36Sopenharmony_ci		     (wqe->processed + srx->fpdu_part_rem != wqe->bytes))) {
20562306a36Sopenharmony_ci		pr_warn("siw: [QP %u]: rresp len: %d != %d\n",
20662306a36Sopenharmony_ci			qp_id(rx_qp(srx)),
20762306a36Sopenharmony_ci			wqe->processed + srx->fpdu_part_rem, wqe->bytes);
20862306a36Sopenharmony_ci		ecode = DDP_ECODE_T_BASE_BOUNDS;
20962306a36Sopenharmony_ci		goto error;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci	return 0;
21262306a36Sopenharmony_cierror:
21362306a36Sopenharmony_ci	siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_DDP,
21462306a36Sopenharmony_ci			   DDP_ETYPE_TAGGED_BUF, ecode, 0);
21562306a36Sopenharmony_ci	return -EINVAL;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/*
21962306a36Sopenharmony_ci * siw_write_check_ntoh()
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci * Check incoming WRITE fragment header against expected
22262306a36Sopenharmony_ci * header values and update expected values for potential next
22362306a36Sopenharmony_ci * fragment
22462306a36Sopenharmony_ci *
22562306a36Sopenharmony_ci * NOTE: This function must be called only if a WRITE DDP segment
22662306a36Sopenharmony_ci *       starts but not for fragmented consecutive pieces of an
22762306a36Sopenharmony_ci *       already started DDP segment.
22862306a36Sopenharmony_ci */
22962306a36Sopenharmony_cistatic int siw_write_check_ntoh(struct siw_rx_stream *srx,
23062306a36Sopenharmony_ci				struct siw_rx_fpdu *frx)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct iwarp_rdma_write *write = &srx->hdr.rwrite;
23362306a36Sopenharmony_ci	enum ddp_ecode ecode;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	u32 sink_stag = be32_to_cpu(write->sink_stag);
23662306a36Sopenharmony_ci	u64 sink_to = be64_to_cpu(write->sink_to);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (frx->first_ddp_seg) {
23962306a36Sopenharmony_ci		srx->ddp_stag = sink_stag;
24062306a36Sopenharmony_ci		srx->ddp_to = sink_to;
24162306a36Sopenharmony_ci		frx->pbl_idx = 0;
24262306a36Sopenharmony_ci	} else {
24362306a36Sopenharmony_ci		if (unlikely(srx->ddp_stag != sink_stag)) {
24462306a36Sopenharmony_ci			pr_warn("siw: [QP %u]: write stag: %08x != %08x\n",
24562306a36Sopenharmony_ci				qp_id(rx_qp(srx)), sink_stag,
24662306a36Sopenharmony_ci				srx->ddp_stag);
24762306a36Sopenharmony_ci			ecode = DDP_ECODE_T_INVALID_STAG;
24862306a36Sopenharmony_ci			goto error;
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci		if (unlikely(srx->ddp_to != sink_to)) {
25162306a36Sopenharmony_ci			pr_warn("siw: [QP %u]: write off: %016llx != %016llx\n",
25262306a36Sopenharmony_ci				qp_id(rx_qp(srx)),
25362306a36Sopenharmony_ci				(unsigned long long)sink_to,
25462306a36Sopenharmony_ci				(unsigned long long)srx->ddp_to);
25562306a36Sopenharmony_ci			ecode = DDP_ECODE_T_BASE_BOUNDS;
25662306a36Sopenharmony_ci			goto error;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_cierror:
26162306a36Sopenharmony_ci	siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_DDP,
26262306a36Sopenharmony_ci			   DDP_ETYPE_TAGGED_BUF, ecode, 0);
26362306a36Sopenharmony_ci	return -EINVAL;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/*
26762306a36Sopenharmony_ci * siw_send_check_ntoh()
26862306a36Sopenharmony_ci *
26962306a36Sopenharmony_ci * Check incoming SEND fragment header against expected
27062306a36Sopenharmony_ci * header values and update expected MSN if no next
27162306a36Sopenharmony_ci * fragment expected
27262306a36Sopenharmony_ci *
27362306a36Sopenharmony_ci * NOTE: This function must be called only if a SEND DDP segment
27462306a36Sopenharmony_ci *       starts but not for fragmented consecutive pieces of an
27562306a36Sopenharmony_ci *       already started DDP segment.
27662306a36Sopenharmony_ci */
27762306a36Sopenharmony_cistatic int siw_send_check_ntoh(struct siw_rx_stream *srx,
27862306a36Sopenharmony_ci			       struct siw_rx_fpdu *frx)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	struct iwarp_send_inv *send = &srx->hdr.send_inv;
28162306a36Sopenharmony_ci	struct siw_wqe *wqe = &frx->wqe_active;
28262306a36Sopenharmony_ci	enum ddp_ecode ecode;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	u32 ddp_msn = be32_to_cpu(send->ddp_msn);
28562306a36Sopenharmony_ci	u32 ddp_mo = be32_to_cpu(send->ddp_mo);
28662306a36Sopenharmony_ci	u32 ddp_qn = be32_to_cpu(send->ddp_qn);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (unlikely(ddp_qn != RDMAP_UNTAGGED_QN_SEND)) {
28962306a36Sopenharmony_ci		pr_warn("siw: [QP %u]: invalid ddp qn %d for send\n",
29062306a36Sopenharmony_ci			qp_id(rx_qp(srx)), ddp_qn);
29162306a36Sopenharmony_ci		ecode = DDP_ECODE_UT_INVALID_QN;
29262306a36Sopenharmony_ci		goto error;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci	if (unlikely(ddp_msn != srx->ddp_msn[RDMAP_UNTAGGED_QN_SEND])) {
29562306a36Sopenharmony_ci		pr_warn("siw: [QP %u]: send msn: %u != %u\n",
29662306a36Sopenharmony_ci			qp_id(rx_qp(srx)), ddp_msn,
29762306a36Sopenharmony_ci			srx->ddp_msn[RDMAP_UNTAGGED_QN_SEND]);
29862306a36Sopenharmony_ci		ecode = DDP_ECODE_UT_INVALID_MSN_RANGE;
29962306a36Sopenharmony_ci		goto error;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci	if (unlikely(ddp_mo != wqe->processed)) {
30262306a36Sopenharmony_ci		pr_warn("siw: [QP %u], send mo: %u != %u\n",
30362306a36Sopenharmony_ci			qp_id(rx_qp(srx)), ddp_mo, wqe->processed);
30462306a36Sopenharmony_ci		ecode = DDP_ECODE_UT_INVALID_MO;
30562306a36Sopenharmony_ci		goto error;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci	if (frx->first_ddp_seg) {
30862306a36Sopenharmony_ci		/* initialize user memory write position */
30962306a36Sopenharmony_ci		frx->sge_idx = 0;
31062306a36Sopenharmony_ci		frx->sge_off = 0;
31162306a36Sopenharmony_ci		frx->pbl_idx = 0;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		/* only valid for SEND_INV and SEND_SE_INV operations */
31462306a36Sopenharmony_ci		srx->inval_stag = be32_to_cpu(send->inval_stag);
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci	if (unlikely(wqe->bytes < wqe->processed + srx->fpdu_part_rem)) {
31762306a36Sopenharmony_ci		siw_dbg_qp(rx_qp(srx), "receive space short: %d - %d < %d\n",
31862306a36Sopenharmony_ci			   wqe->bytes, wqe->processed, srx->fpdu_part_rem);
31962306a36Sopenharmony_ci		wqe->wc_status = SIW_WC_LOC_LEN_ERR;
32062306a36Sopenharmony_ci		ecode = DDP_ECODE_UT_INVALID_MSN_NOBUF;
32162306a36Sopenharmony_ci		goto error;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	return 0;
32462306a36Sopenharmony_cierror:
32562306a36Sopenharmony_ci	siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_DDP,
32662306a36Sopenharmony_ci			   DDP_ETYPE_UNTAGGED_BUF, ecode, 0);
32762306a36Sopenharmony_ci	return -EINVAL;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic struct siw_wqe *siw_rqe_get(struct siw_qp *qp)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct siw_rqe *rqe;
33362306a36Sopenharmony_ci	struct siw_srq *srq;
33462306a36Sopenharmony_ci	struct siw_wqe *wqe = NULL;
33562306a36Sopenharmony_ci	bool srq_event = false;
33662306a36Sopenharmony_ci	unsigned long flags;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	srq = qp->srq;
33962306a36Sopenharmony_ci	if (srq) {
34062306a36Sopenharmony_ci		spin_lock_irqsave(&srq->lock, flags);
34162306a36Sopenharmony_ci		if (unlikely(!srq->num_rqe))
34262306a36Sopenharmony_ci			goto out;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		rqe = &srq->recvq[srq->rq_get % srq->num_rqe];
34562306a36Sopenharmony_ci	} else {
34662306a36Sopenharmony_ci		if (unlikely(!qp->recvq))
34762306a36Sopenharmony_ci			goto out;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		rqe = &qp->recvq[qp->rq_get % qp->attrs.rq_size];
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci	if (likely(rqe->flags == SIW_WQE_VALID)) {
35262306a36Sopenharmony_ci		int num_sge = rqe->num_sge;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		if (likely(num_sge <= SIW_MAX_SGE)) {
35562306a36Sopenharmony_ci			int i = 0;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci			wqe = rx_wqe(&qp->rx_untagged);
35862306a36Sopenharmony_ci			rx_type(wqe) = SIW_OP_RECEIVE;
35962306a36Sopenharmony_ci			wqe->wr_status = SIW_WR_INPROGRESS;
36062306a36Sopenharmony_ci			wqe->bytes = 0;
36162306a36Sopenharmony_ci			wqe->processed = 0;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci			wqe->rqe.id = rqe->id;
36462306a36Sopenharmony_ci			wqe->rqe.num_sge = num_sge;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci			while (i < num_sge) {
36762306a36Sopenharmony_ci				wqe->rqe.sge[i].laddr = rqe->sge[i].laddr;
36862306a36Sopenharmony_ci				wqe->rqe.sge[i].lkey = rqe->sge[i].lkey;
36962306a36Sopenharmony_ci				wqe->rqe.sge[i].length = rqe->sge[i].length;
37062306a36Sopenharmony_ci				wqe->bytes += wqe->rqe.sge[i].length;
37162306a36Sopenharmony_ci				wqe->mem[i] = NULL;
37262306a36Sopenharmony_ci				i++;
37362306a36Sopenharmony_ci			}
37462306a36Sopenharmony_ci			/* can be re-used by appl */
37562306a36Sopenharmony_ci			smp_store_mb(rqe->flags, 0);
37662306a36Sopenharmony_ci		} else {
37762306a36Sopenharmony_ci			siw_dbg_qp(qp, "too many sge's: %d\n", rqe->num_sge);
37862306a36Sopenharmony_ci			if (srq)
37962306a36Sopenharmony_ci				spin_unlock_irqrestore(&srq->lock, flags);
38062306a36Sopenharmony_ci			return NULL;
38162306a36Sopenharmony_ci		}
38262306a36Sopenharmony_ci		if (!srq) {
38362306a36Sopenharmony_ci			qp->rq_get++;
38462306a36Sopenharmony_ci		} else {
38562306a36Sopenharmony_ci			if (srq->armed) {
38662306a36Sopenharmony_ci				/* Test SRQ limit */
38762306a36Sopenharmony_ci				u32 off = (srq->rq_get + srq->limit) %
38862306a36Sopenharmony_ci					  srq->num_rqe;
38962306a36Sopenharmony_ci				struct siw_rqe *rqe2 = &srq->recvq[off];
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci				if (!(rqe2->flags & SIW_WQE_VALID)) {
39262306a36Sopenharmony_ci					srq->armed = false;
39362306a36Sopenharmony_ci					srq_event = true;
39462306a36Sopenharmony_ci				}
39562306a36Sopenharmony_ci			}
39662306a36Sopenharmony_ci			srq->rq_get++;
39762306a36Sopenharmony_ci		}
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ciout:
40062306a36Sopenharmony_ci	if (srq) {
40162306a36Sopenharmony_ci		spin_unlock_irqrestore(&srq->lock, flags);
40262306a36Sopenharmony_ci		if (srq_event)
40362306a36Sopenharmony_ci			siw_srq_event(srq, IB_EVENT_SRQ_LIMIT_REACHED);
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci	return wqe;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci/*
40962306a36Sopenharmony_ci * siw_proc_send:
41062306a36Sopenharmony_ci *
41162306a36Sopenharmony_ci * Process one incoming SEND and place data into memory referenced by
41262306a36Sopenharmony_ci * receive wqe.
41362306a36Sopenharmony_ci *
41462306a36Sopenharmony_ci * Function supports partially received sends (suspending/resuming
41562306a36Sopenharmony_ci * current receive wqe processing)
41662306a36Sopenharmony_ci *
41762306a36Sopenharmony_ci * return value:
41862306a36Sopenharmony_ci *	0:       reached the end of a DDP segment
41962306a36Sopenharmony_ci *	-EAGAIN: to be called again to finish the DDP segment
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_ciint siw_proc_send(struct siw_qp *qp)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
42462306a36Sopenharmony_ci	struct siw_rx_fpdu *frx = &qp->rx_untagged;
42562306a36Sopenharmony_ci	struct siw_wqe *wqe;
42662306a36Sopenharmony_ci	u32 data_bytes; /* all data bytes available */
42762306a36Sopenharmony_ci	u32 rcvd_bytes; /* sum of data bytes rcvd */
42862306a36Sopenharmony_ci	int rv = 0;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (frx->first_ddp_seg) {
43162306a36Sopenharmony_ci		wqe = siw_rqe_get(qp);
43262306a36Sopenharmony_ci		if (unlikely(!wqe)) {
43362306a36Sopenharmony_ci			siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
43462306a36Sopenharmony_ci					   DDP_ETYPE_UNTAGGED_BUF,
43562306a36Sopenharmony_ci					   DDP_ECODE_UT_INVALID_MSN_NOBUF, 0);
43662306a36Sopenharmony_ci			return -ENOENT;
43762306a36Sopenharmony_ci		}
43862306a36Sopenharmony_ci	} else {
43962306a36Sopenharmony_ci		wqe = rx_wqe(frx);
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci	if (srx->state == SIW_GET_DATA_START) {
44262306a36Sopenharmony_ci		rv = siw_send_check_ntoh(srx, frx);
44362306a36Sopenharmony_ci		if (unlikely(rv)) {
44462306a36Sopenharmony_ci			siw_qp_event(qp, IB_EVENT_QP_FATAL);
44562306a36Sopenharmony_ci			return rv;
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci		if (!srx->fpdu_part_rem) /* zero length SEND */
44862306a36Sopenharmony_ci			return 0;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci	data_bytes = min(srx->fpdu_part_rem, srx->skb_new);
45162306a36Sopenharmony_ci	rcvd_bytes = 0;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/* A zero length SEND will skip below loop */
45462306a36Sopenharmony_ci	while (data_bytes) {
45562306a36Sopenharmony_ci		struct ib_pd *pd;
45662306a36Sopenharmony_ci		struct siw_mem **mem, *mem_p;
45762306a36Sopenharmony_ci		struct siw_sge *sge;
45862306a36Sopenharmony_ci		u32 sge_bytes; /* data bytes avail for SGE */
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		sge = &wqe->rqe.sge[frx->sge_idx];
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci		if (!sge->length) {
46362306a36Sopenharmony_ci			/* just skip empty sge's */
46462306a36Sopenharmony_ci			frx->sge_idx++;
46562306a36Sopenharmony_ci			frx->sge_off = 0;
46662306a36Sopenharmony_ci			frx->pbl_idx = 0;
46762306a36Sopenharmony_ci			continue;
46862306a36Sopenharmony_ci		}
46962306a36Sopenharmony_ci		sge_bytes = min(data_bytes, sge->length - frx->sge_off);
47062306a36Sopenharmony_ci		mem = &wqe->mem[frx->sge_idx];
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		/*
47362306a36Sopenharmony_ci		 * check with QP's PD if no SRQ present, SRQ's PD otherwise
47462306a36Sopenharmony_ci		 */
47562306a36Sopenharmony_ci		pd = qp->srq == NULL ? qp->pd : qp->srq->base_srq.pd;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		rv = siw_check_sge(pd, sge, mem, IB_ACCESS_LOCAL_WRITE,
47862306a36Sopenharmony_ci				   frx->sge_off, sge_bytes);
47962306a36Sopenharmony_ci		if (unlikely(rv)) {
48062306a36Sopenharmony_ci			siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
48162306a36Sopenharmony_ci					   DDP_ETYPE_CATASTROPHIC,
48262306a36Sopenharmony_ci					   DDP_ECODE_CATASTROPHIC, 0);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci			siw_qp_event(qp, IB_EVENT_QP_ACCESS_ERR);
48562306a36Sopenharmony_ci			break;
48662306a36Sopenharmony_ci		}
48762306a36Sopenharmony_ci		mem_p = *mem;
48862306a36Sopenharmony_ci		if (mem_p->mem_obj == NULL)
48962306a36Sopenharmony_ci			rv = siw_rx_kva(srx,
49062306a36Sopenharmony_ci				ib_virt_dma_to_ptr(sge->laddr + frx->sge_off),
49162306a36Sopenharmony_ci				sge_bytes);
49262306a36Sopenharmony_ci		else if (!mem_p->is_pbl)
49362306a36Sopenharmony_ci			rv = siw_rx_umem(srx, mem_p->umem,
49462306a36Sopenharmony_ci					 sge->laddr + frx->sge_off, sge_bytes);
49562306a36Sopenharmony_ci		else
49662306a36Sopenharmony_ci			rv = siw_rx_pbl(srx, &frx->pbl_idx, mem_p,
49762306a36Sopenharmony_ci					sge->laddr + frx->sge_off, sge_bytes);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		if (unlikely(rv != sge_bytes)) {
50062306a36Sopenharmony_ci			wqe->processed += rcvd_bytes;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci			siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
50362306a36Sopenharmony_ci					   DDP_ETYPE_CATASTROPHIC,
50462306a36Sopenharmony_ci					   DDP_ECODE_CATASTROPHIC, 0);
50562306a36Sopenharmony_ci			return -EINVAL;
50662306a36Sopenharmony_ci		}
50762306a36Sopenharmony_ci		frx->sge_off += rv;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		if (frx->sge_off == sge->length) {
51062306a36Sopenharmony_ci			frx->sge_idx++;
51162306a36Sopenharmony_ci			frx->sge_off = 0;
51262306a36Sopenharmony_ci			frx->pbl_idx = 0;
51362306a36Sopenharmony_ci		}
51462306a36Sopenharmony_ci		data_bytes -= rv;
51562306a36Sopenharmony_ci		rcvd_bytes += rv;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		srx->fpdu_part_rem -= rv;
51862306a36Sopenharmony_ci		srx->fpdu_part_rcvd += rv;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci	wqe->processed += rcvd_bytes;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (!srx->fpdu_part_rem)
52362306a36Sopenharmony_ci		return 0;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	return (rv < 0) ? rv : -EAGAIN;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci/*
52962306a36Sopenharmony_ci * siw_proc_write:
53062306a36Sopenharmony_ci *
53162306a36Sopenharmony_ci * Place incoming WRITE after referencing and checking target buffer
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci * Function supports partially received WRITEs (suspending/resuming
53462306a36Sopenharmony_ci * current receive processing)
53562306a36Sopenharmony_ci *
53662306a36Sopenharmony_ci * return value:
53762306a36Sopenharmony_ci *	0:       reached the end of a DDP segment
53862306a36Sopenharmony_ci *	-EAGAIN: to be called again to finish the DDP segment
53962306a36Sopenharmony_ci */
54062306a36Sopenharmony_ciint siw_proc_write(struct siw_qp *qp)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
54362306a36Sopenharmony_ci	struct siw_rx_fpdu *frx = &qp->rx_tagged;
54462306a36Sopenharmony_ci	struct siw_mem *mem;
54562306a36Sopenharmony_ci	int bytes, rv;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (srx->state == SIW_GET_DATA_START) {
54862306a36Sopenharmony_ci		if (!srx->fpdu_part_rem) /* zero length WRITE */
54962306a36Sopenharmony_ci			return 0;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		rv = siw_write_check_ntoh(srx, frx);
55262306a36Sopenharmony_ci		if (unlikely(rv)) {
55362306a36Sopenharmony_ci			siw_qp_event(qp, IB_EVENT_QP_FATAL);
55462306a36Sopenharmony_ci			return rv;
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci	bytes = min(srx->fpdu_part_rem, srx->skb_new);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (frx->first_ddp_seg) {
56062306a36Sopenharmony_ci		struct siw_wqe *wqe = rx_wqe(frx);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		rx_mem(frx) = siw_mem_id2obj(qp->sdev, srx->ddp_stag >> 8);
56362306a36Sopenharmony_ci		if (unlikely(!rx_mem(frx))) {
56462306a36Sopenharmony_ci			siw_dbg_qp(qp,
56562306a36Sopenharmony_ci				   "sink stag not found/invalid, stag 0x%08x\n",
56662306a36Sopenharmony_ci				   srx->ddp_stag);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci			siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
56962306a36Sopenharmony_ci					   DDP_ETYPE_TAGGED_BUF,
57062306a36Sopenharmony_ci					   DDP_ECODE_T_INVALID_STAG, 0);
57162306a36Sopenharmony_ci			return -EINVAL;
57262306a36Sopenharmony_ci		}
57362306a36Sopenharmony_ci		wqe->rqe.num_sge = 1;
57462306a36Sopenharmony_ci		rx_type(wqe) = SIW_OP_WRITE;
57562306a36Sopenharmony_ci		wqe->wr_status = SIW_WR_INPROGRESS;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci	mem = rx_mem(frx);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	/*
58062306a36Sopenharmony_ci	 * Check if application re-registered memory with different
58162306a36Sopenharmony_ci	 * key field of STag.
58262306a36Sopenharmony_ci	 */
58362306a36Sopenharmony_ci	if (unlikely(mem->stag != srx->ddp_stag)) {
58462306a36Sopenharmony_ci		siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
58562306a36Sopenharmony_ci				   DDP_ETYPE_TAGGED_BUF,
58662306a36Sopenharmony_ci				   DDP_ECODE_T_INVALID_STAG, 0);
58762306a36Sopenharmony_ci		return -EINVAL;
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci	rv = siw_check_mem(qp->pd, mem, srx->ddp_to + srx->fpdu_part_rcvd,
59062306a36Sopenharmony_ci			   IB_ACCESS_REMOTE_WRITE, bytes);
59162306a36Sopenharmony_ci	if (unlikely(rv)) {
59262306a36Sopenharmony_ci		siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
59362306a36Sopenharmony_ci				   DDP_ETYPE_TAGGED_BUF, siw_tagged_error(-rv),
59462306a36Sopenharmony_ci				   0);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		siw_qp_event(qp, IB_EVENT_QP_ACCESS_ERR);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		return -EINVAL;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (mem->mem_obj == NULL)
60262306a36Sopenharmony_ci		rv = siw_rx_kva(srx,
60362306a36Sopenharmony_ci			(void *)(uintptr_t)(srx->ddp_to + srx->fpdu_part_rcvd),
60462306a36Sopenharmony_ci			bytes);
60562306a36Sopenharmony_ci	else if (!mem->is_pbl)
60662306a36Sopenharmony_ci		rv = siw_rx_umem(srx, mem->umem,
60762306a36Sopenharmony_ci				 srx->ddp_to + srx->fpdu_part_rcvd, bytes);
60862306a36Sopenharmony_ci	else
60962306a36Sopenharmony_ci		rv = siw_rx_pbl(srx, &frx->pbl_idx, mem,
61062306a36Sopenharmony_ci				srx->ddp_to + srx->fpdu_part_rcvd, bytes);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (unlikely(rv != bytes)) {
61362306a36Sopenharmony_ci		siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
61462306a36Sopenharmony_ci				   DDP_ETYPE_CATASTROPHIC,
61562306a36Sopenharmony_ci				   DDP_ECODE_CATASTROPHIC, 0);
61662306a36Sopenharmony_ci		return -EINVAL;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci	srx->fpdu_part_rem -= rv;
61962306a36Sopenharmony_ci	srx->fpdu_part_rcvd += rv;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	if (!srx->fpdu_part_rem) {
62262306a36Sopenharmony_ci		srx->ddp_to += srx->fpdu_part_rcvd;
62362306a36Sopenharmony_ci		return 0;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci	return -EAGAIN;
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci/*
62962306a36Sopenharmony_ci * Inbound RREQ's cannot carry user data.
63062306a36Sopenharmony_ci */
63162306a36Sopenharmony_ciint siw_proc_rreq(struct siw_qp *qp)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	if (!srx->fpdu_part_rem)
63662306a36Sopenharmony_ci		return 0;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	pr_warn("siw: [QP %u]: rreq with mpa len %d\n", qp_id(qp),
63962306a36Sopenharmony_ci		be16_to_cpu(srx->hdr.ctrl.mpa_len));
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return -EPROTO;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci/*
64562306a36Sopenharmony_ci * siw_init_rresp:
64662306a36Sopenharmony_ci *
64762306a36Sopenharmony_ci * Process inbound RDMA READ REQ. Produce a pseudo READ RESPONSE WQE.
64862306a36Sopenharmony_ci * Put it at the tail of the IRQ, if there is another WQE currently in
64962306a36Sopenharmony_ci * transmit processing. If not, make it the current WQE to be processed
65062306a36Sopenharmony_ci * and schedule transmit processing.
65162306a36Sopenharmony_ci *
65262306a36Sopenharmony_ci * Can be called from softirq context and from process
65362306a36Sopenharmony_ci * context (RREAD socket loopback case!)
65462306a36Sopenharmony_ci *
65562306a36Sopenharmony_ci * return value:
65662306a36Sopenharmony_ci *	0:      success,
65762306a36Sopenharmony_ci *		failure code otherwise
65862306a36Sopenharmony_ci */
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic int siw_init_rresp(struct siw_qp *qp, struct siw_rx_stream *srx)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	struct siw_wqe *tx_work = tx_wqe(qp);
66362306a36Sopenharmony_ci	struct siw_sqe *resp;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	uint64_t raddr = be64_to_cpu(srx->hdr.rreq.sink_to),
66662306a36Sopenharmony_ci		 laddr = be64_to_cpu(srx->hdr.rreq.source_to);
66762306a36Sopenharmony_ci	uint32_t length = be32_to_cpu(srx->hdr.rreq.read_size),
66862306a36Sopenharmony_ci		 lkey = be32_to_cpu(srx->hdr.rreq.source_stag),
66962306a36Sopenharmony_ci		 rkey = be32_to_cpu(srx->hdr.rreq.sink_stag),
67062306a36Sopenharmony_ci		 msn = be32_to_cpu(srx->hdr.rreq.ddp_msn);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	int run_sq = 1, rv = 0;
67362306a36Sopenharmony_ci	unsigned long flags;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (unlikely(msn != srx->ddp_msn[RDMAP_UNTAGGED_QN_RDMA_READ])) {
67662306a36Sopenharmony_ci		siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
67762306a36Sopenharmony_ci				   DDP_ETYPE_UNTAGGED_BUF,
67862306a36Sopenharmony_ci				   DDP_ECODE_UT_INVALID_MSN_RANGE, 0);
67962306a36Sopenharmony_ci		return -EPROTO;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci	spin_lock_irqsave(&qp->sq_lock, flags);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (unlikely(!qp->attrs.irq_size)) {
68462306a36Sopenharmony_ci		run_sq = 0;
68562306a36Sopenharmony_ci		goto error_irq;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci	if (tx_work->wr_status == SIW_WR_IDLE) {
68862306a36Sopenharmony_ci		/*
68962306a36Sopenharmony_ci		 * immediately schedule READ response w/o
69062306a36Sopenharmony_ci		 * consuming IRQ entry: IRQ must be empty.
69162306a36Sopenharmony_ci		 */
69262306a36Sopenharmony_ci		tx_work->processed = 0;
69362306a36Sopenharmony_ci		tx_work->mem[0] = NULL;
69462306a36Sopenharmony_ci		tx_work->wr_status = SIW_WR_QUEUED;
69562306a36Sopenharmony_ci		resp = &tx_work->sqe;
69662306a36Sopenharmony_ci	} else {
69762306a36Sopenharmony_ci		resp = irq_alloc_free(qp);
69862306a36Sopenharmony_ci		run_sq = 0;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci	if (likely(resp)) {
70162306a36Sopenharmony_ci		resp->opcode = SIW_OP_READ_RESPONSE;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci		resp->sge[0].length = length;
70462306a36Sopenharmony_ci		resp->sge[0].laddr = laddr;
70562306a36Sopenharmony_ci		resp->sge[0].lkey = lkey;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci		/* Keep aside message sequence number for potential
70862306a36Sopenharmony_ci		 * error reporting during Read Response generation.
70962306a36Sopenharmony_ci		 */
71062306a36Sopenharmony_ci		resp->sge[1].length = msn;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci		resp->raddr = raddr;
71362306a36Sopenharmony_ci		resp->rkey = rkey;
71462306a36Sopenharmony_ci		resp->num_sge = length ? 1 : 0;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci		/* RRESP now valid as current TX wqe or placed into IRQ */
71762306a36Sopenharmony_ci		smp_store_mb(resp->flags, SIW_WQE_VALID);
71862306a36Sopenharmony_ci	} else {
71962306a36Sopenharmony_cierror_irq:
72062306a36Sopenharmony_ci		pr_warn("siw: [QP %u]: IRQ exceeded or null, size %d\n",
72162306a36Sopenharmony_ci			qp_id(qp), qp->attrs.irq_size);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		siw_init_terminate(qp, TERM_ERROR_LAYER_RDMAP,
72462306a36Sopenharmony_ci				   RDMAP_ETYPE_REMOTE_OPERATION,
72562306a36Sopenharmony_ci				   RDMAP_ECODE_CATASTROPHIC_STREAM, 0);
72662306a36Sopenharmony_ci		rv = -EPROTO;
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	spin_unlock_irqrestore(&qp->sq_lock, flags);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (run_sq)
73262306a36Sopenharmony_ci		rv = siw_sq_start(qp);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	return rv;
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci/*
73862306a36Sopenharmony_ci * Only called at start of Read.Resonse processing.
73962306a36Sopenharmony_ci * Transfer pending Read from tip of ORQ into currrent rx wqe,
74062306a36Sopenharmony_ci * but keep ORQ entry valid until Read.Response processing done.
74162306a36Sopenharmony_ci * No Queue locking needed.
74262306a36Sopenharmony_ci */
74362306a36Sopenharmony_cistatic int siw_orqe_start_rx(struct siw_qp *qp)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	struct siw_sqe *orqe;
74662306a36Sopenharmony_ci	struct siw_wqe *wqe = NULL;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if (unlikely(!qp->attrs.orq_size))
74962306a36Sopenharmony_ci		return -EPROTO;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	/* make sure ORQ indices are current */
75262306a36Sopenharmony_ci	smp_mb();
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	orqe = orq_get_current(qp);
75562306a36Sopenharmony_ci	if (READ_ONCE(orqe->flags) & SIW_WQE_VALID) {
75662306a36Sopenharmony_ci		/* RRESP is a TAGGED RDMAP operation */
75762306a36Sopenharmony_ci		wqe = rx_wqe(&qp->rx_tagged);
75862306a36Sopenharmony_ci		wqe->sqe.id = orqe->id;
75962306a36Sopenharmony_ci		wqe->sqe.opcode = orqe->opcode;
76062306a36Sopenharmony_ci		wqe->sqe.sge[0].laddr = orqe->sge[0].laddr;
76162306a36Sopenharmony_ci		wqe->sqe.sge[0].lkey = orqe->sge[0].lkey;
76262306a36Sopenharmony_ci		wqe->sqe.sge[0].length = orqe->sge[0].length;
76362306a36Sopenharmony_ci		wqe->sqe.flags = orqe->flags;
76462306a36Sopenharmony_ci		wqe->sqe.num_sge = 1;
76562306a36Sopenharmony_ci		wqe->bytes = orqe->sge[0].length;
76662306a36Sopenharmony_ci		wqe->processed = 0;
76762306a36Sopenharmony_ci		wqe->mem[0] = NULL;
76862306a36Sopenharmony_ci		/* make sure WQE is completely written before valid */
76962306a36Sopenharmony_ci		smp_wmb();
77062306a36Sopenharmony_ci		wqe->wr_status = SIW_WR_INPROGRESS;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		return 0;
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci	return -EPROTO;
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci/*
77862306a36Sopenharmony_ci * siw_proc_rresp:
77962306a36Sopenharmony_ci *
78062306a36Sopenharmony_ci * Place incoming RRESP data into memory referenced by RREQ WQE
78162306a36Sopenharmony_ci * which is at the tip of the ORQ
78262306a36Sopenharmony_ci *
78362306a36Sopenharmony_ci * Function supports partially received RRESP's (suspending/resuming
78462306a36Sopenharmony_ci * current receive processing)
78562306a36Sopenharmony_ci */
78662306a36Sopenharmony_ciint siw_proc_rresp(struct siw_qp *qp)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
78962306a36Sopenharmony_ci	struct siw_rx_fpdu *frx = &qp->rx_tagged;
79062306a36Sopenharmony_ci	struct siw_wqe *wqe = rx_wqe(frx);
79162306a36Sopenharmony_ci	struct siw_mem **mem, *mem_p;
79262306a36Sopenharmony_ci	struct siw_sge *sge;
79362306a36Sopenharmony_ci	int bytes, rv;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (frx->first_ddp_seg) {
79662306a36Sopenharmony_ci		if (unlikely(wqe->wr_status != SIW_WR_IDLE)) {
79762306a36Sopenharmony_ci			pr_warn("siw: [QP %u]: proc RRESP: status %d, op %d\n",
79862306a36Sopenharmony_ci				qp_id(qp), wqe->wr_status, wqe->sqe.opcode);
79962306a36Sopenharmony_ci			rv = -EPROTO;
80062306a36Sopenharmony_ci			goto error_term;
80162306a36Sopenharmony_ci		}
80262306a36Sopenharmony_ci		/*
80362306a36Sopenharmony_ci		 * fetch pending RREQ from orq
80462306a36Sopenharmony_ci		 */
80562306a36Sopenharmony_ci		rv = siw_orqe_start_rx(qp);
80662306a36Sopenharmony_ci		if (rv) {
80762306a36Sopenharmony_ci			pr_warn("siw: [QP %u]: ORQ empty, size %d\n",
80862306a36Sopenharmony_ci				qp_id(qp), qp->attrs.orq_size);
80962306a36Sopenharmony_ci			goto error_term;
81062306a36Sopenharmony_ci		}
81162306a36Sopenharmony_ci		rv = siw_rresp_check_ntoh(srx, frx);
81262306a36Sopenharmony_ci		if (unlikely(rv)) {
81362306a36Sopenharmony_ci			siw_qp_event(qp, IB_EVENT_QP_FATAL);
81462306a36Sopenharmony_ci			return rv;
81562306a36Sopenharmony_ci		}
81662306a36Sopenharmony_ci	} else {
81762306a36Sopenharmony_ci		if (unlikely(wqe->wr_status != SIW_WR_INPROGRESS)) {
81862306a36Sopenharmony_ci			pr_warn("siw: [QP %u]: resume RRESP: status %d\n",
81962306a36Sopenharmony_ci				qp_id(qp), wqe->wr_status);
82062306a36Sopenharmony_ci			rv = -EPROTO;
82162306a36Sopenharmony_ci			goto error_term;
82262306a36Sopenharmony_ci		}
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci	if (!srx->fpdu_part_rem) /* zero length RRESPONSE */
82562306a36Sopenharmony_ci		return 0;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	sge = wqe->sqe.sge; /* there is only one */
82862306a36Sopenharmony_ci	mem = &wqe->mem[0];
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	if (!(*mem)) {
83162306a36Sopenharmony_ci		/*
83262306a36Sopenharmony_ci		 * check target memory which resolves memory on first fragment
83362306a36Sopenharmony_ci		 */
83462306a36Sopenharmony_ci		rv = siw_check_sge(qp->pd, sge, mem, IB_ACCESS_LOCAL_WRITE, 0,
83562306a36Sopenharmony_ci				   wqe->bytes);
83662306a36Sopenharmony_ci		if (unlikely(rv)) {
83762306a36Sopenharmony_ci			siw_dbg_qp(qp, "target mem check: %d\n", rv);
83862306a36Sopenharmony_ci			wqe->wc_status = SIW_WC_LOC_PROT_ERR;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci			siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
84162306a36Sopenharmony_ci					   DDP_ETYPE_TAGGED_BUF,
84262306a36Sopenharmony_ci					   siw_tagged_error(-rv), 0);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci			siw_qp_event(qp, IB_EVENT_QP_ACCESS_ERR);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci			return -EINVAL;
84762306a36Sopenharmony_ci		}
84862306a36Sopenharmony_ci	}
84962306a36Sopenharmony_ci	mem_p = *mem;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	bytes = min(srx->fpdu_part_rem, srx->skb_new);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (mem_p->mem_obj == NULL)
85462306a36Sopenharmony_ci		rv = siw_rx_kva(srx,
85562306a36Sopenharmony_ci			ib_virt_dma_to_ptr(sge->laddr + wqe->processed),
85662306a36Sopenharmony_ci			bytes);
85762306a36Sopenharmony_ci	else if (!mem_p->is_pbl)
85862306a36Sopenharmony_ci		rv = siw_rx_umem(srx, mem_p->umem, sge->laddr + wqe->processed,
85962306a36Sopenharmony_ci				 bytes);
86062306a36Sopenharmony_ci	else
86162306a36Sopenharmony_ci		rv = siw_rx_pbl(srx, &frx->pbl_idx, mem_p,
86262306a36Sopenharmony_ci				sge->laddr + wqe->processed, bytes);
86362306a36Sopenharmony_ci	if (rv != bytes) {
86462306a36Sopenharmony_ci		wqe->wc_status = SIW_WC_GENERAL_ERR;
86562306a36Sopenharmony_ci		rv = -EINVAL;
86662306a36Sopenharmony_ci		goto error_term;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci	srx->fpdu_part_rem -= rv;
86962306a36Sopenharmony_ci	srx->fpdu_part_rcvd += rv;
87062306a36Sopenharmony_ci	wqe->processed += rv;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	if (!srx->fpdu_part_rem) {
87362306a36Sopenharmony_ci		srx->ddp_to += srx->fpdu_part_rcvd;
87462306a36Sopenharmony_ci		return 0;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci	return -EAGAIN;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cierror_term:
87962306a36Sopenharmony_ci	siw_init_terminate(qp, TERM_ERROR_LAYER_DDP, DDP_ETYPE_CATASTROPHIC,
88062306a36Sopenharmony_ci			   DDP_ECODE_CATASTROPHIC, 0);
88162306a36Sopenharmony_ci	return rv;
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ciint siw_proc_terminate(struct siw_qp *qp)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
88762306a36Sopenharmony_ci	struct sk_buff *skb = srx->skb;
88862306a36Sopenharmony_ci	struct iwarp_terminate *term = &srx->hdr.terminate;
88962306a36Sopenharmony_ci	union iwarp_hdr term_info;
89062306a36Sopenharmony_ci	u8 *infop = (u8 *)&term_info;
89162306a36Sopenharmony_ci	enum rdma_opcode op;
89262306a36Sopenharmony_ci	u16 to_copy = sizeof(struct iwarp_ctrl);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	pr_warn("siw: got TERMINATE. layer %d, type %d, code %d\n",
89562306a36Sopenharmony_ci		__rdmap_term_layer(term), __rdmap_term_etype(term),
89662306a36Sopenharmony_ci		__rdmap_term_ecode(term));
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (be32_to_cpu(term->ddp_qn) != RDMAP_UNTAGGED_QN_TERMINATE ||
89962306a36Sopenharmony_ci	    be32_to_cpu(term->ddp_msn) !=
90062306a36Sopenharmony_ci		    qp->rx_stream.ddp_msn[RDMAP_UNTAGGED_QN_TERMINATE] ||
90162306a36Sopenharmony_ci	    be32_to_cpu(term->ddp_mo) != 0) {
90262306a36Sopenharmony_ci		pr_warn("siw: rx bogus TERM [QN x%08x, MSN x%08x, MO x%08x]\n",
90362306a36Sopenharmony_ci			be32_to_cpu(term->ddp_qn), be32_to_cpu(term->ddp_msn),
90462306a36Sopenharmony_ci			be32_to_cpu(term->ddp_mo));
90562306a36Sopenharmony_ci		return -ECONNRESET;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci	/*
90862306a36Sopenharmony_ci	 * Receive remaining pieces of TERM if indicated
90962306a36Sopenharmony_ci	 */
91062306a36Sopenharmony_ci	if (!term->flag_m)
91162306a36Sopenharmony_ci		return -ECONNRESET;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	/* Do not take the effort to reassemble a network fragmented
91462306a36Sopenharmony_ci	 * TERM message
91562306a36Sopenharmony_ci	 */
91662306a36Sopenharmony_ci	if (srx->skb_new < sizeof(struct iwarp_ctrl_tagged))
91762306a36Sopenharmony_ci		return -ECONNRESET;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	memset(infop, 0, sizeof(term_info));
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	skb_copy_bits(skb, srx->skb_offset, infop, to_copy);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	op = __rdmap_get_opcode(&term_info.ctrl);
92462306a36Sopenharmony_ci	if (op >= RDMAP_TERMINATE)
92562306a36Sopenharmony_ci		goto out;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	infop += to_copy;
92862306a36Sopenharmony_ci	srx->skb_offset += to_copy;
92962306a36Sopenharmony_ci	srx->skb_new -= to_copy;
93062306a36Sopenharmony_ci	srx->skb_copied += to_copy;
93162306a36Sopenharmony_ci	srx->fpdu_part_rcvd += to_copy;
93262306a36Sopenharmony_ci	srx->fpdu_part_rem -= to_copy;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	to_copy = iwarp_pktinfo[op].hdr_len - to_copy;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	/* Again, no network fragmented TERM's */
93762306a36Sopenharmony_ci	if (to_copy + MPA_CRC_SIZE > srx->skb_new)
93862306a36Sopenharmony_ci		return -ECONNRESET;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	skb_copy_bits(skb, srx->skb_offset, infop, to_copy);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	if (term->flag_r) {
94362306a36Sopenharmony_ci		siw_dbg_qp(qp, "TERM reports RDMAP hdr type %u, len %u (%s)\n",
94462306a36Sopenharmony_ci			   op, be16_to_cpu(term_info.ctrl.mpa_len),
94562306a36Sopenharmony_ci			   term->flag_m ? "valid" : "invalid");
94662306a36Sopenharmony_ci	} else if (term->flag_d) {
94762306a36Sopenharmony_ci		siw_dbg_qp(qp, "TERM reports DDP hdr type %u, len %u (%s)\n",
94862306a36Sopenharmony_ci			   op, be16_to_cpu(term_info.ctrl.mpa_len),
94962306a36Sopenharmony_ci			   term->flag_m ? "valid" : "invalid");
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ciout:
95262306a36Sopenharmony_ci	srx->skb_new -= to_copy;
95362306a36Sopenharmony_ci	srx->skb_offset += to_copy;
95462306a36Sopenharmony_ci	srx->skb_copied += to_copy;
95562306a36Sopenharmony_ci	srx->fpdu_part_rcvd += to_copy;
95662306a36Sopenharmony_ci	srx->fpdu_part_rem -= to_copy;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	return -ECONNRESET;
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_cistatic int siw_get_trailer(struct siw_qp *qp, struct siw_rx_stream *srx)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	struct sk_buff *skb = srx->skb;
96462306a36Sopenharmony_ci	int avail = min(srx->skb_new, srx->fpdu_part_rem);
96562306a36Sopenharmony_ci	u8 *tbuf = (u8 *)&srx->trailer.crc - srx->pad;
96662306a36Sopenharmony_ci	__wsum crc_in, crc_own = 0;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	siw_dbg_qp(qp, "expected %d, available %d, pad %u\n",
96962306a36Sopenharmony_ci		   srx->fpdu_part_rem, srx->skb_new, srx->pad);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	skb_copy_bits(skb, srx->skb_offset, tbuf, avail);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	srx->skb_new -= avail;
97462306a36Sopenharmony_ci	srx->skb_offset += avail;
97562306a36Sopenharmony_ci	srx->skb_copied += avail;
97662306a36Sopenharmony_ci	srx->fpdu_part_rem -= avail;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	if (srx->fpdu_part_rem)
97962306a36Sopenharmony_ci		return -EAGAIN;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	if (!srx->mpa_crc_hd)
98262306a36Sopenharmony_ci		return 0;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (srx->pad)
98562306a36Sopenharmony_ci		crypto_shash_update(srx->mpa_crc_hd, tbuf, srx->pad);
98662306a36Sopenharmony_ci	/*
98762306a36Sopenharmony_ci	 * CRC32 is computed, transmitted and received directly in NBO,
98862306a36Sopenharmony_ci	 * so there's never a reason to convert byte order.
98962306a36Sopenharmony_ci	 */
99062306a36Sopenharmony_ci	crypto_shash_final(srx->mpa_crc_hd, (u8 *)&crc_own);
99162306a36Sopenharmony_ci	crc_in = (__force __wsum)srx->trailer.crc;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	if (unlikely(crc_in != crc_own)) {
99462306a36Sopenharmony_ci		pr_warn("siw: crc error. in: %08x, own %08x, op %u\n",
99562306a36Sopenharmony_ci			crc_in, crc_own, qp->rx_stream.rdmap_op);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci		siw_init_terminate(qp, TERM_ERROR_LAYER_LLP,
99862306a36Sopenharmony_ci				   LLP_ETYPE_MPA,
99962306a36Sopenharmony_ci				   LLP_ECODE_RECEIVED_CRC, 0);
100062306a36Sopenharmony_ci		return -EINVAL;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci	return 0;
100362306a36Sopenharmony_ci}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci#define MIN_DDP_HDR sizeof(struct iwarp_ctrl_tagged)
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic int siw_get_hdr(struct siw_rx_stream *srx)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	struct sk_buff *skb = srx->skb;
101062306a36Sopenharmony_ci	struct siw_qp *qp = rx_qp(srx);
101162306a36Sopenharmony_ci	struct iwarp_ctrl *c_hdr = &srx->hdr.ctrl;
101262306a36Sopenharmony_ci	struct siw_rx_fpdu *frx;
101362306a36Sopenharmony_ci	u8 opcode;
101462306a36Sopenharmony_ci	int bytes;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	if (srx->fpdu_part_rcvd < MIN_DDP_HDR) {
101762306a36Sopenharmony_ci		/*
101862306a36Sopenharmony_ci		 * copy a mimimum sized (tagged) DDP frame control part
101962306a36Sopenharmony_ci		 */
102062306a36Sopenharmony_ci		bytes = min_t(int, srx->skb_new,
102162306a36Sopenharmony_ci			      MIN_DDP_HDR - srx->fpdu_part_rcvd);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci		skb_copy_bits(skb, srx->skb_offset,
102462306a36Sopenharmony_ci			      (char *)c_hdr + srx->fpdu_part_rcvd, bytes);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci		srx->fpdu_part_rcvd += bytes;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci		srx->skb_new -= bytes;
102962306a36Sopenharmony_ci		srx->skb_offset += bytes;
103062306a36Sopenharmony_ci		srx->skb_copied += bytes;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		if (srx->fpdu_part_rcvd < MIN_DDP_HDR)
103362306a36Sopenharmony_ci			return -EAGAIN;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci		if (unlikely(__ddp_get_version(c_hdr) != DDP_VERSION)) {
103662306a36Sopenharmony_ci			enum ddp_etype etype;
103762306a36Sopenharmony_ci			enum ddp_ecode ecode;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci			pr_warn("siw: received ddp version unsupported %d\n",
104062306a36Sopenharmony_ci				__ddp_get_version(c_hdr));
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci			if (c_hdr->ddp_rdmap_ctrl & DDP_FLAG_TAGGED) {
104362306a36Sopenharmony_ci				etype = DDP_ETYPE_TAGGED_BUF;
104462306a36Sopenharmony_ci				ecode = DDP_ECODE_T_VERSION;
104562306a36Sopenharmony_ci			} else {
104662306a36Sopenharmony_ci				etype = DDP_ETYPE_UNTAGGED_BUF;
104762306a36Sopenharmony_ci				ecode = DDP_ECODE_UT_VERSION;
104862306a36Sopenharmony_ci			}
104962306a36Sopenharmony_ci			siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_DDP,
105062306a36Sopenharmony_ci					   etype, ecode, 0);
105162306a36Sopenharmony_ci			return -EINVAL;
105262306a36Sopenharmony_ci		}
105362306a36Sopenharmony_ci		if (unlikely(__rdmap_get_version(c_hdr) != RDMAP_VERSION)) {
105462306a36Sopenharmony_ci			pr_warn("siw: received rdmap version unsupported %d\n",
105562306a36Sopenharmony_ci				__rdmap_get_version(c_hdr));
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci			siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_RDMAP,
105862306a36Sopenharmony_ci					   RDMAP_ETYPE_REMOTE_OPERATION,
105962306a36Sopenharmony_ci					   RDMAP_ECODE_VERSION, 0);
106062306a36Sopenharmony_ci			return -EINVAL;
106162306a36Sopenharmony_ci		}
106262306a36Sopenharmony_ci		opcode = __rdmap_get_opcode(c_hdr);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci		if (opcode > RDMAP_TERMINATE) {
106562306a36Sopenharmony_ci			pr_warn("siw: received unknown packet type %u\n",
106662306a36Sopenharmony_ci				opcode);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci			siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_RDMAP,
106962306a36Sopenharmony_ci					   RDMAP_ETYPE_REMOTE_OPERATION,
107062306a36Sopenharmony_ci					   RDMAP_ECODE_OPCODE, 0);
107162306a36Sopenharmony_ci			return -EINVAL;
107262306a36Sopenharmony_ci		}
107362306a36Sopenharmony_ci		siw_dbg_qp(rx_qp(srx), "new header, opcode %u\n", opcode);
107462306a36Sopenharmony_ci	} else {
107562306a36Sopenharmony_ci		opcode = __rdmap_get_opcode(c_hdr);
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci	set_rx_fpdu_context(qp, opcode);
107862306a36Sopenharmony_ci	frx = qp->rx_fpdu;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	/*
108162306a36Sopenharmony_ci	 * Figure out len of current hdr: variable length of
108262306a36Sopenharmony_ci	 * iwarp hdr may force us to copy hdr information in
108362306a36Sopenharmony_ci	 * two steps. Only tagged DDP messages are already
108462306a36Sopenharmony_ci	 * completely received.
108562306a36Sopenharmony_ci	 */
108662306a36Sopenharmony_ci	if (iwarp_pktinfo[opcode].hdr_len > sizeof(struct iwarp_ctrl_tagged)) {
108762306a36Sopenharmony_ci		int hdrlen = iwarp_pktinfo[opcode].hdr_len;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci		bytes = min_t(int, hdrlen - MIN_DDP_HDR, srx->skb_new);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci		skb_copy_bits(skb, srx->skb_offset,
109262306a36Sopenharmony_ci			      (char *)c_hdr + srx->fpdu_part_rcvd, bytes);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci		srx->fpdu_part_rcvd += bytes;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci		srx->skb_new -= bytes;
109762306a36Sopenharmony_ci		srx->skb_offset += bytes;
109862306a36Sopenharmony_ci		srx->skb_copied += bytes;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci		if (srx->fpdu_part_rcvd < hdrlen)
110162306a36Sopenharmony_ci			return -EAGAIN;
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	/*
110562306a36Sopenharmony_ci	 * DDP/RDMAP header receive completed. Check if the current
110662306a36Sopenharmony_ci	 * DDP segment starts a new RDMAP message or continues a previously
110762306a36Sopenharmony_ci	 * started RDMAP message.
110862306a36Sopenharmony_ci	 *
110962306a36Sopenharmony_ci	 * Alternating reception of DDP segments (or FPDUs) from incomplete
111062306a36Sopenharmony_ci	 * tagged and untagged RDMAP messages is supported, as long as
111162306a36Sopenharmony_ci	 * the current tagged or untagged message gets eventually completed
111262306a36Sopenharmony_ci	 * w/o intersection from another message of the same type
111362306a36Sopenharmony_ci	 * (tagged/untagged). E.g., a WRITE can get intersected by a SEND,
111462306a36Sopenharmony_ci	 * but not by a READ RESPONSE etc.
111562306a36Sopenharmony_ci	 */
111662306a36Sopenharmony_ci	if (srx->mpa_crc_hd) {
111762306a36Sopenharmony_ci		/*
111862306a36Sopenharmony_ci		 * Restart CRC computation
111962306a36Sopenharmony_ci		 */
112062306a36Sopenharmony_ci		crypto_shash_init(srx->mpa_crc_hd);
112162306a36Sopenharmony_ci		crypto_shash_update(srx->mpa_crc_hd, (u8 *)c_hdr,
112262306a36Sopenharmony_ci				    srx->fpdu_part_rcvd);
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci	if (frx->more_ddp_segs) {
112562306a36Sopenharmony_ci		frx->first_ddp_seg = 0;
112662306a36Sopenharmony_ci		if (frx->prev_rdmap_op != opcode) {
112762306a36Sopenharmony_ci			pr_warn("siw: packet intersection: %u : %u\n",
112862306a36Sopenharmony_ci				frx->prev_rdmap_op, opcode);
112962306a36Sopenharmony_ci			/*
113062306a36Sopenharmony_ci			 * The last inbound RDMA operation of same type
113162306a36Sopenharmony_ci			 * (tagged or untagged) is left unfinished.
113262306a36Sopenharmony_ci			 * To complete it in error, make it the current
113362306a36Sopenharmony_ci			 * operation again, even with the header already
113462306a36Sopenharmony_ci			 * overwritten. For error handling, only the opcode
113562306a36Sopenharmony_ci			 * and current rx context are relevant.
113662306a36Sopenharmony_ci			 */
113762306a36Sopenharmony_ci			set_rx_fpdu_context(qp, frx->prev_rdmap_op);
113862306a36Sopenharmony_ci			__rdmap_set_opcode(c_hdr, frx->prev_rdmap_op);
113962306a36Sopenharmony_ci			return -EPROTO;
114062306a36Sopenharmony_ci		}
114162306a36Sopenharmony_ci	} else {
114262306a36Sopenharmony_ci		frx->prev_rdmap_op = opcode;
114362306a36Sopenharmony_ci		frx->first_ddp_seg = 1;
114462306a36Sopenharmony_ci	}
114562306a36Sopenharmony_ci	frx->more_ddp_segs = c_hdr->ddp_rdmap_ctrl & DDP_FLAG_LAST ? 0 : 1;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	return 0;
114862306a36Sopenharmony_ci}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_cistatic int siw_check_tx_fence(struct siw_qp *qp)
115162306a36Sopenharmony_ci{
115262306a36Sopenharmony_ci	struct siw_wqe *tx_waiting = tx_wqe(qp);
115362306a36Sopenharmony_ci	struct siw_sqe *rreq;
115462306a36Sopenharmony_ci	int resume_tx = 0, rv = 0;
115562306a36Sopenharmony_ci	unsigned long flags;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	spin_lock_irqsave(&qp->orq_lock, flags);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	/* free current orq entry */
116062306a36Sopenharmony_ci	rreq = orq_get_current(qp);
116162306a36Sopenharmony_ci	WRITE_ONCE(rreq->flags, 0);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	qp->orq_get++;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	if (qp->tx_ctx.orq_fence) {
116662306a36Sopenharmony_ci		if (unlikely(tx_waiting->wr_status != SIW_WR_QUEUED)) {
116762306a36Sopenharmony_ci			pr_warn("siw: [QP %u]: fence resume: bad status %d\n",
116862306a36Sopenharmony_ci				qp_id(qp), tx_waiting->wr_status);
116962306a36Sopenharmony_ci			rv = -EPROTO;
117062306a36Sopenharmony_ci			goto out;
117162306a36Sopenharmony_ci		}
117262306a36Sopenharmony_ci		/* resume SQ processing, if possible */
117362306a36Sopenharmony_ci		if (tx_waiting->sqe.opcode == SIW_OP_READ ||
117462306a36Sopenharmony_ci		    tx_waiting->sqe.opcode == SIW_OP_READ_LOCAL_INV) {
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci			/* SQ processing was stopped because of a full ORQ */
117762306a36Sopenharmony_ci			rreq = orq_get_free(qp);
117862306a36Sopenharmony_ci			if (unlikely(!rreq)) {
117962306a36Sopenharmony_ci				pr_warn("siw: [QP %u]: no ORQE\n", qp_id(qp));
118062306a36Sopenharmony_ci				rv = -EPROTO;
118162306a36Sopenharmony_ci				goto out;
118262306a36Sopenharmony_ci			}
118362306a36Sopenharmony_ci			siw_read_to_orq(rreq, &tx_waiting->sqe);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci			qp->orq_put++;
118662306a36Sopenharmony_ci			qp->tx_ctx.orq_fence = 0;
118762306a36Sopenharmony_ci			resume_tx = 1;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci		} else if (siw_orq_empty(qp)) {
119062306a36Sopenharmony_ci			/*
119162306a36Sopenharmony_ci			 * SQ processing was stopped by fenced work request.
119262306a36Sopenharmony_ci			 * Resume since all previous Read's are now completed.
119362306a36Sopenharmony_ci			 */
119462306a36Sopenharmony_ci			qp->tx_ctx.orq_fence = 0;
119562306a36Sopenharmony_ci			resume_tx = 1;
119662306a36Sopenharmony_ci		}
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ciout:
119962306a36Sopenharmony_ci	spin_unlock_irqrestore(&qp->orq_lock, flags);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	if (resume_tx)
120262306a36Sopenharmony_ci		rv = siw_sq_start(qp);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	return rv;
120562306a36Sopenharmony_ci}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci/*
120862306a36Sopenharmony_ci * siw_rdmap_complete()
120962306a36Sopenharmony_ci *
121062306a36Sopenharmony_ci * Complete processing of an RDMA message after receiving all
121162306a36Sopenharmony_ci * DDP segmens or ABort processing after encountering error case.
121262306a36Sopenharmony_ci *
121362306a36Sopenharmony_ci *   o SENDs + RRESPs will need for completion,
121462306a36Sopenharmony_ci *   o RREQs need for  READ RESPONSE initialization
121562306a36Sopenharmony_ci *   o WRITEs need memory dereferencing
121662306a36Sopenharmony_ci *
121762306a36Sopenharmony_ci * TODO: Failed WRITEs need local error to be surfaced.
121862306a36Sopenharmony_ci */
121962306a36Sopenharmony_cistatic int siw_rdmap_complete(struct siw_qp *qp, int error)
122062306a36Sopenharmony_ci{
122162306a36Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
122262306a36Sopenharmony_ci	struct siw_wqe *wqe = rx_wqe(qp->rx_fpdu);
122362306a36Sopenharmony_ci	enum siw_wc_status wc_status = wqe->wc_status;
122462306a36Sopenharmony_ci	u8 opcode = __rdmap_get_opcode(&srx->hdr.ctrl);
122562306a36Sopenharmony_ci	int rv = 0;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	switch (opcode) {
122862306a36Sopenharmony_ci	case RDMAP_SEND_SE:
122962306a36Sopenharmony_ci	case RDMAP_SEND_SE_INVAL:
123062306a36Sopenharmony_ci		wqe->rqe.flags |= SIW_WQE_SOLICITED;
123162306a36Sopenharmony_ci		fallthrough;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	case RDMAP_SEND:
123462306a36Sopenharmony_ci	case RDMAP_SEND_INVAL:
123562306a36Sopenharmony_ci		if (wqe->wr_status == SIW_WR_IDLE)
123662306a36Sopenharmony_ci			break;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci		srx->ddp_msn[RDMAP_UNTAGGED_QN_SEND]++;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci		if (error != 0 && wc_status == SIW_WC_SUCCESS)
124162306a36Sopenharmony_ci			wc_status = SIW_WC_GENERAL_ERR;
124262306a36Sopenharmony_ci		/*
124362306a36Sopenharmony_ci		 * Handle STag invalidation request
124462306a36Sopenharmony_ci		 */
124562306a36Sopenharmony_ci		if (wc_status == SIW_WC_SUCCESS &&
124662306a36Sopenharmony_ci		    (opcode == RDMAP_SEND_INVAL ||
124762306a36Sopenharmony_ci		     opcode == RDMAP_SEND_SE_INVAL)) {
124862306a36Sopenharmony_ci			rv = siw_invalidate_stag(qp->pd, srx->inval_stag);
124962306a36Sopenharmony_ci			if (rv) {
125062306a36Sopenharmony_ci				siw_init_terminate(
125162306a36Sopenharmony_ci					qp, TERM_ERROR_LAYER_RDMAP,
125262306a36Sopenharmony_ci					rv == -EACCES ?
125362306a36Sopenharmony_ci						RDMAP_ETYPE_REMOTE_PROTECTION :
125462306a36Sopenharmony_ci						RDMAP_ETYPE_REMOTE_OPERATION,
125562306a36Sopenharmony_ci					RDMAP_ECODE_CANNOT_INVALIDATE, 0);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci				wc_status = SIW_WC_REM_INV_REQ_ERR;
125862306a36Sopenharmony_ci			}
125962306a36Sopenharmony_ci			rv = siw_rqe_complete(qp, &wqe->rqe, wqe->processed,
126062306a36Sopenharmony_ci					      rv ? 0 : srx->inval_stag,
126162306a36Sopenharmony_ci					      wc_status);
126262306a36Sopenharmony_ci		} else {
126362306a36Sopenharmony_ci			rv = siw_rqe_complete(qp, &wqe->rqe, wqe->processed,
126462306a36Sopenharmony_ci					      0, wc_status);
126562306a36Sopenharmony_ci		}
126662306a36Sopenharmony_ci		siw_wqe_put_mem(wqe, SIW_OP_RECEIVE);
126762306a36Sopenharmony_ci		break;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	case RDMAP_RDMA_READ_RESP:
127062306a36Sopenharmony_ci		if (wqe->wr_status == SIW_WR_IDLE)
127162306a36Sopenharmony_ci			break;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci		if (error != 0) {
127462306a36Sopenharmony_ci			if ((srx->state == SIW_GET_HDR &&
127562306a36Sopenharmony_ci			     qp->rx_fpdu->first_ddp_seg) || error == -ENODATA)
127662306a36Sopenharmony_ci				/* possible RREQ in ORQ left untouched */
127762306a36Sopenharmony_ci				break;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci			if (wc_status == SIW_WC_SUCCESS)
128062306a36Sopenharmony_ci				wc_status = SIW_WC_GENERAL_ERR;
128162306a36Sopenharmony_ci		} else if (rdma_is_kernel_res(&qp->base_qp.res) &&
128262306a36Sopenharmony_ci			   rx_type(wqe) == SIW_OP_READ_LOCAL_INV) {
128362306a36Sopenharmony_ci			/*
128462306a36Sopenharmony_ci			 * Handle any STag invalidation request
128562306a36Sopenharmony_ci			 */
128662306a36Sopenharmony_ci			rv = siw_invalidate_stag(qp->pd, wqe->sqe.sge[0].lkey);
128762306a36Sopenharmony_ci			if (rv) {
128862306a36Sopenharmony_ci				siw_init_terminate(qp, TERM_ERROR_LAYER_RDMAP,
128962306a36Sopenharmony_ci						   RDMAP_ETYPE_CATASTROPHIC,
129062306a36Sopenharmony_ci						   RDMAP_ECODE_UNSPECIFIED, 0);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci				if (wc_status == SIW_WC_SUCCESS) {
129362306a36Sopenharmony_ci					wc_status = SIW_WC_GENERAL_ERR;
129462306a36Sopenharmony_ci					error = rv;
129562306a36Sopenharmony_ci				}
129662306a36Sopenharmony_ci			}
129762306a36Sopenharmony_ci		}
129862306a36Sopenharmony_ci		/*
129962306a36Sopenharmony_ci		 * All errors turn the wqe into signalled.
130062306a36Sopenharmony_ci		 */
130162306a36Sopenharmony_ci		if ((wqe->sqe.flags & SIW_WQE_SIGNALLED) || error != 0)
130262306a36Sopenharmony_ci			rv = siw_sqe_complete(qp, &wqe->sqe, wqe->processed,
130362306a36Sopenharmony_ci					      wc_status);
130462306a36Sopenharmony_ci		siw_wqe_put_mem(wqe, SIW_OP_READ);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci		if (!error) {
130762306a36Sopenharmony_ci			rv = siw_check_tx_fence(qp);
130862306a36Sopenharmony_ci		} else {
130962306a36Sopenharmony_ci			/* Disable current ORQ element */
131062306a36Sopenharmony_ci			if (qp->attrs.orq_size)
131162306a36Sopenharmony_ci				WRITE_ONCE(orq_get_current(qp)->flags, 0);
131262306a36Sopenharmony_ci		}
131362306a36Sopenharmony_ci		break;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	case RDMAP_RDMA_READ_REQ:
131662306a36Sopenharmony_ci		if (!error) {
131762306a36Sopenharmony_ci			rv = siw_init_rresp(qp, srx);
131862306a36Sopenharmony_ci			srx->ddp_msn[RDMAP_UNTAGGED_QN_RDMA_READ]++;
131962306a36Sopenharmony_ci		}
132062306a36Sopenharmony_ci		break;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	case RDMAP_RDMA_WRITE:
132362306a36Sopenharmony_ci		if (wqe->wr_status == SIW_WR_IDLE)
132462306a36Sopenharmony_ci			break;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci		/*
132762306a36Sopenharmony_ci		 * Free References from memory object if
132862306a36Sopenharmony_ci		 * attached to receive context (inbound WRITE).
132962306a36Sopenharmony_ci		 * While a zero-length WRITE is allowed,
133062306a36Sopenharmony_ci		 * no memory reference got created.
133162306a36Sopenharmony_ci		 */
133262306a36Sopenharmony_ci		if (rx_mem(&qp->rx_tagged)) {
133362306a36Sopenharmony_ci			siw_mem_put(rx_mem(&qp->rx_tagged));
133462306a36Sopenharmony_ci			rx_mem(&qp->rx_tagged) = NULL;
133562306a36Sopenharmony_ci		}
133662306a36Sopenharmony_ci		break;
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	default:
133962306a36Sopenharmony_ci		break;
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci	wqe->wr_status = SIW_WR_IDLE;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	return rv;
134462306a36Sopenharmony_ci}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci/*
134762306a36Sopenharmony_ci * siw_tcp_rx_data()
134862306a36Sopenharmony_ci *
134962306a36Sopenharmony_ci * Main routine to consume inbound TCP payload
135062306a36Sopenharmony_ci *
135162306a36Sopenharmony_ci * @rd_desc:	read descriptor
135262306a36Sopenharmony_ci * @skb:	socket buffer
135362306a36Sopenharmony_ci * @off:	offset in skb
135462306a36Sopenharmony_ci * @len:	skb->len - offset : payload in skb
135562306a36Sopenharmony_ci */
135662306a36Sopenharmony_ciint siw_tcp_rx_data(read_descriptor_t *rd_desc, struct sk_buff *skb,
135762306a36Sopenharmony_ci		    unsigned int off, size_t len)
135862306a36Sopenharmony_ci{
135962306a36Sopenharmony_ci	struct siw_qp *qp = rd_desc->arg.data;
136062306a36Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
136162306a36Sopenharmony_ci	int rv;
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	srx->skb = skb;
136462306a36Sopenharmony_ci	srx->skb_new = skb->len - off;
136562306a36Sopenharmony_ci	srx->skb_offset = off;
136662306a36Sopenharmony_ci	srx->skb_copied = 0;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	siw_dbg_qp(qp, "new data, len %d\n", srx->skb_new);
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	while (srx->skb_new) {
137162306a36Sopenharmony_ci		int run_completion = 1;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci		if (unlikely(srx->rx_suspend)) {
137462306a36Sopenharmony_ci			/* Do not process any more data */
137562306a36Sopenharmony_ci			srx->skb_copied += srx->skb_new;
137662306a36Sopenharmony_ci			break;
137762306a36Sopenharmony_ci		}
137862306a36Sopenharmony_ci		switch (srx->state) {
137962306a36Sopenharmony_ci		case SIW_GET_HDR:
138062306a36Sopenharmony_ci			rv = siw_get_hdr(srx);
138162306a36Sopenharmony_ci			if (!rv) {
138262306a36Sopenharmony_ci				srx->fpdu_part_rem =
138362306a36Sopenharmony_ci					be16_to_cpu(srx->hdr.ctrl.mpa_len) -
138462306a36Sopenharmony_ci					srx->fpdu_part_rcvd + MPA_HDR_SIZE;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci				if (srx->fpdu_part_rem)
138762306a36Sopenharmony_ci					srx->pad = -srx->fpdu_part_rem & 0x3;
138862306a36Sopenharmony_ci				else
138962306a36Sopenharmony_ci					srx->pad = 0;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci				srx->state = SIW_GET_DATA_START;
139262306a36Sopenharmony_ci				srx->fpdu_part_rcvd = 0;
139362306a36Sopenharmony_ci			}
139462306a36Sopenharmony_ci			break;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci		case SIW_GET_DATA_MORE:
139762306a36Sopenharmony_ci			/*
139862306a36Sopenharmony_ci			 * Another data fragment of the same DDP segment.
139962306a36Sopenharmony_ci			 * Setting first_ddp_seg = 0 avoids repeating
140062306a36Sopenharmony_ci			 * initializations that shall occur only once per
140162306a36Sopenharmony_ci			 * DDP segment.
140262306a36Sopenharmony_ci			 */
140362306a36Sopenharmony_ci			qp->rx_fpdu->first_ddp_seg = 0;
140462306a36Sopenharmony_ci			fallthrough;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci		case SIW_GET_DATA_START:
140762306a36Sopenharmony_ci			/*
140862306a36Sopenharmony_ci			 * Headers will be checked by the opcode-specific
140962306a36Sopenharmony_ci			 * data receive function below.
141062306a36Sopenharmony_ci			 */
141162306a36Sopenharmony_ci			rv = iwarp_pktinfo[qp->rx_stream.rdmap_op].rx_data(qp);
141262306a36Sopenharmony_ci			if (!rv) {
141362306a36Sopenharmony_ci				int mpa_len =
141462306a36Sopenharmony_ci					be16_to_cpu(srx->hdr.ctrl.mpa_len)
141562306a36Sopenharmony_ci					+ MPA_HDR_SIZE;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci				srx->fpdu_part_rem = (-mpa_len & 0x3)
141862306a36Sopenharmony_ci						      + MPA_CRC_SIZE;
141962306a36Sopenharmony_ci				srx->fpdu_part_rcvd = 0;
142062306a36Sopenharmony_ci				srx->state = SIW_GET_TRAILER;
142162306a36Sopenharmony_ci			} else {
142262306a36Sopenharmony_ci				if (unlikely(rv == -ECONNRESET))
142362306a36Sopenharmony_ci					run_completion = 0;
142462306a36Sopenharmony_ci				else
142562306a36Sopenharmony_ci					srx->state = SIW_GET_DATA_MORE;
142662306a36Sopenharmony_ci			}
142762306a36Sopenharmony_ci			break;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci		case SIW_GET_TRAILER:
143062306a36Sopenharmony_ci			/*
143162306a36Sopenharmony_ci			 * read CRC + any padding
143262306a36Sopenharmony_ci			 */
143362306a36Sopenharmony_ci			rv = siw_get_trailer(qp, srx);
143462306a36Sopenharmony_ci			if (likely(!rv)) {
143562306a36Sopenharmony_ci				/*
143662306a36Sopenharmony_ci				 * FPDU completed.
143762306a36Sopenharmony_ci				 * complete RDMAP message if last fragment
143862306a36Sopenharmony_ci				 */
143962306a36Sopenharmony_ci				srx->state = SIW_GET_HDR;
144062306a36Sopenharmony_ci				srx->fpdu_part_rcvd = 0;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci				if (!(srx->hdr.ctrl.ddp_rdmap_ctrl &
144362306a36Sopenharmony_ci				      DDP_FLAG_LAST))
144462306a36Sopenharmony_ci					/* more frags */
144562306a36Sopenharmony_ci					break;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci				rv = siw_rdmap_complete(qp, 0);
144862306a36Sopenharmony_ci				run_completion = 0;
144962306a36Sopenharmony_ci			}
145062306a36Sopenharmony_ci			break;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci		default:
145362306a36Sopenharmony_ci			pr_warn("QP[%u]: RX out of state\n", qp_id(qp));
145462306a36Sopenharmony_ci			rv = -EPROTO;
145562306a36Sopenharmony_ci			run_completion = 0;
145662306a36Sopenharmony_ci		}
145762306a36Sopenharmony_ci		if (unlikely(rv != 0 && rv != -EAGAIN)) {
145862306a36Sopenharmony_ci			if ((srx->state > SIW_GET_HDR ||
145962306a36Sopenharmony_ci			     qp->rx_fpdu->more_ddp_segs) && run_completion)
146062306a36Sopenharmony_ci				siw_rdmap_complete(qp, rv);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci			siw_dbg_qp(qp, "rx error %d, rx state %d\n", rv,
146362306a36Sopenharmony_ci				   srx->state);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci			siw_qp_cm_drop(qp, 1);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci			break;
146862306a36Sopenharmony_ci		}
146962306a36Sopenharmony_ci		if (rv) {
147062306a36Sopenharmony_ci			siw_dbg_qp(qp, "fpdu fragment, state %d, missing %d\n",
147162306a36Sopenharmony_ci				   srx->state, srx->fpdu_part_rem);
147262306a36Sopenharmony_ci			break;
147362306a36Sopenharmony_ci		}
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci	return srx->skb_copied;
147662306a36Sopenharmony_ci}
1477