18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/* Authors: Bernard Metzler <bmt@zurich.ibm.com> */
48c2ecf20Sopenharmony_ci/* Copyright (c) 2008-2019, IBM Corporation */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/errno.h>
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <linux/net.h>
98c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
108c2ecf20Sopenharmony_ci#include <linux/highmem.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <rdma/iw_cm.h>
138c2ecf20Sopenharmony_ci#include <rdma/ib_verbs.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "siw.h"
168c2ecf20Sopenharmony_ci#include "siw_verbs.h"
178c2ecf20Sopenharmony_ci#include "siw_mem.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci * siw_rx_umem()
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * Receive data of @len into target referenced by @dest_addr.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * @srx:	Receive Context
258c2ecf20Sopenharmony_ci * @umem:	siw representation of target memory
268c2ecf20Sopenharmony_ci * @dest_addr:	user virtual address
278c2ecf20Sopenharmony_ci * @len:	number of bytes to place
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_cistatic int siw_rx_umem(struct siw_rx_stream *srx, struct siw_umem *umem,
308c2ecf20Sopenharmony_ci		       u64 dest_addr, int len)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	int copied = 0;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	while (len) {
358c2ecf20Sopenharmony_ci		struct page *p;
368c2ecf20Sopenharmony_ci		int pg_off, bytes, rv;
378c2ecf20Sopenharmony_ci		void *dest;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci		p = siw_get_upage(umem, dest_addr);
408c2ecf20Sopenharmony_ci		if (unlikely(!p)) {
418c2ecf20Sopenharmony_ci			pr_warn("siw: %s: [QP %u]: bogus addr: %pK, %pK\n",
428c2ecf20Sopenharmony_ci				__func__, qp_id(rx_qp(srx)),
438c2ecf20Sopenharmony_ci				(void *)(uintptr_t)dest_addr,
448c2ecf20Sopenharmony_ci				(void *)(uintptr_t)umem->fp_addr);
458c2ecf20Sopenharmony_ci			/* siw internal error */
468c2ecf20Sopenharmony_ci			srx->skb_copied += copied;
478c2ecf20Sopenharmony_ci			srx->skb_new -= copied;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci			return -EFAULT;
508c2ecf20Sopenharmony_ci		}
518c2ecf20Sopenharmony_ci		pg_off = dest_addr & ~PAGE_MASK;
528c2ecf20Sopenharmony_ci		bytes = min(len, (int)PAGE_SIZE - pg_off);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci		siw_dbg_qp(rx_qp(srx), "page %pK, bytes=%u\n", p, bytes);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci		dest = kmap_atomic(p);
578c2ecf20Sopenharmony_ci		rv = skb_copy_bits(srx->skb, srx->skb_offset, dest + pg_off,
588c2ecf20Sopenharmony_ci				   bytes);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci		if (unlikely(rv)) {
618c2ecf20Sopenharmony_ci			kunmap_atomic(dest);
628c2ecf20Sopenharmony_ci			srx->skb_copied += copied;
638c2ecf20Sopenharmony_ci			srx->skb_new -= copied;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci			pr_warn("siw: [QP %u]: %s, len %d, page %p, rv %d\n",
668c2ecf20Sopenharmony_ci				qp_id(rx_qp(srx)), __func__, len, p, rv);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci			return -EFAULT;
698c2ecf20Sopenharmony_ci		}
708c2ecf20Sopenharmony_ci		if (srx->mpa_crc_hd) {
718c2ecf20Sopenharmony_ci			if (rdma_is_kernel_res(&rx_qp(srx)->base_qp.res)) {
728c2ecf20Sopenharmony_ci				crypto_shash_update(srx->mpa_crc_hd,
738c2ecf20Sopenharmony_ci					(u8 *)(dest + pg_off), bytes);
748c2ecf20Sopenharmony_ci				kunmap_atomic(dest);
758c2ecf20Sopenharmony_ci			} else {
768c2ecf20Sopenharmony_ci				kunmap_atomic(dest);
778c2ecf20Sopenharmony_ci				/*
788c2ecf20Sopenharmony_ci				 * Do CRC on original, not target buffer.
798c2ecf20Sopenharmony_ci				 * Some user land applications may
808c2ecf20Sopenharmony_ci				 * concurrently write the target buffer,
818c2ecf20Sopenharmony_ci				 * which would yield a broken CRC.
828c2ecf20Sopenharmony_ci				 * Walking the skb twice is very ineffcient.
838c2ecf20Sopenharmony_ci				 * Folding the CRC into skb_copy_bits()
848c2ecf20Sopenharmony_ci				 * would be much better, but is currently
858c2ecf20Sopenharmony_ci				 * not supported.
868c2ecf20Sopenharmony_ci				 */
878c2ecf20Sopenharmony_ci				siw_crc_skb(srx, bytes);
888c2ecf20Sopenharmony_ci			}
898c2ecf20Sopenharmony_ci		} else {
908c2ecf20Sopenharmony_ci			kunmap_atomic(dest);
918c2ecf20Sopenharmony_ci		}
928c2ecf20Sopenharmony_ci		srx->skb_offset += bytes;
938c2ecf20Sopenharmony_ci		copied += bytes;
948c2ecf20Sopenharmony_ci		len -= bytes;
958c2ecf20Sopenharmony_ci		dest_addr += bytes;
968c2ecf20Sopenharmony_ci		pg_off = 0;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci	srx->skb_copied += copied;
998c2ecf20Sopenharmony_ci	srx->skb_new -= copied;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return copied;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int siw_rx_kva(struct siw_rx_stream *srx, void *kva, int len)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	int rv;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	siw_dbg_qp(rx_qp(srx), "kva: 0x%pK, len: %u\n", kva, len);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	rv = skb_copy_bits(srx->skb, srx->skb_offset, kva, len);
1118c2ecf20Sopenharmony_ci	if (unlikely(rv)) {
1128c2ecf20Sopenharmony_ci		pr_warn("siw: [QP %u]: %s, len %d, kva 0x%pK, rv %d\n",
1138c2ecf20Sopenharmony_ci			qp_id(rx_qp(srx)), __func__, len, kva, rv);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci		return rv;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	if (srx->mpa_crc_hd)
1188c2ecf20Sopenharmony_ci		crypto_shash_update(srx->mpa_crc_hd, (u8 *)kva, len);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	srx->skb_offset += len;
1218c2ecf20Sopenharmony_ci	srx->skb_copied += len;
1228c2ecf20Sopenharmony_ci	srx->skb_new -= len;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return len;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int siw_rx_pbl(struct siw_rx_stream *srx, int *pbl_idx,
1288c2ecf20Sopenharmony_ci		      struct siw_mem *mem, u64 addr, int len)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct siw_pbl *pbl = mem->pbl;
1318c2ecf20Sopenharmony_ci	u64 offset = addr - mem->va;
1328c2ecf20Sopenharmony_ci	int copied = 0;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	while (len) {
1358c2ecf20Sopenharmony_ci		int bytes;
1368c2ecf20Sopenharmony_ci		dma_addr_t buf_addr =
1378c2ecf20Sopenharmony_ci			siw_pbl_get_buffer(pbl, offset, &bytes, pbl_idx);
1388c2ecf20Sopenharmony_ci		if (!buf_addr)
1398c2ecf20Sopenharmony_ci			break;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		bytes = min(bytes, len);
1428c2ecf20Sopenharmony_ci		if (siw_rx_kva(srx, (void *)(uintptr_t)buf_addr, bytes) ==
1438c2ecf20Sopenharmony_ci		    bytes) {
1448c2ecf20Sopenharmony_ci			copied += bytes;
1458c2ecf20Sopenharmony_ci			offset += bytes;
1468c2ecf20Sopenharmony_ci			len -= bytes;
1478c2ecf20Sopenharmony_ci		} else {
1488c2ecf20Sopenharmony_ci			break;
1498c2ecf20Sopenharmony_ci		}
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci	return copied;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/*
1558c2ecf20Sopenharmony_ci * siw_rresp_check_ntoh()
1568c2ecf20Sopenharmony_ci *
1578c2ecf20Sopenharmony_ci * Check incoming RRESP fragment header against expected
1588c2ecf20Sopenharmony_ci * header values and update expected values for potential next
1598c2ecf20Sopenharmony_ci * fragment.
1608c2ecf20Sopenharmony_ci *
1618c2ecf20Sopenharmony_ci * NOTE: This function must be called only if a RRESP DDP segment
1628c2ecf20Sopenharmony_ci *       starts but not for fragmented consecutive pieces of an
1638c2ecf20Sopenharmony_ci *       already started DDP segment.
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistatic int siw_rresp_check_ntoh(struct siw_rx_stream *srx,
1668c2ecf20Sopenharmony_ci				struct siw_rx_fpdu *frx)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct iwarp_rdma_rresp *rresp = &srx->hdr.rresp;
1698c2ecf20Sopenharmony_ci	struct siw_wqe *wqe = &frx->wqe_active;
1708c2ecf20Sopenharmony_ci	enum ddp_ecode ecode;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	u32 sink_stag = be32_to_cpu(rresp->sink_stag);
1738c2ecf20Sopenharmony_ci	u64 sink_to = be64_to_cpu(rresp->sink_to);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (frx->first_ddp_seg) {
1768c2ecf20Sopenharmony_ci		srx->ddp_stag = wqe->sqe.sge[0].lkey;
1778c2ecf20Sopenharmony_ci		srx->ddp_to = wqe->sqe.sge[0].laddr;
1788c2ecf20Sopenharmony_ci		frx->pbl_idx = 0;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci	/* Below checks extend beyond the semantics of DDP, and
1818c2ecf20Sopenharmony_ci	 * into RDMAP:
1828c2ecf20Sopenharmony_ci	 * We check if the read response matches exactly the
1838c2ecf20Sopenharmony_ci	 * read request which was send to the remote peer to
1848c2ecf20Sopenharmony_ci	 * trigger this read response. RFC5040/5041 do not
1858c2ecf20Sopenharmony_ci	 * always have a proper error code for the detected
1868c2ecf20Sopenharmony_ci	 * error cases. We choose 'base or bounds error' for
1878c2ecf20Sopenharmony_ci	 * cases where the inbound STag is valid, but offset
1888c2ecf20Sopenharmony_ci	 * or length do not match our response receive state.
1898c2ecf20Sopenharmony_ci	 */
1908c2ecf20Sopenharmony_ci	if (unlikely(srx->ddp_stag != sink_stag)) {
1918c2ecf20Sopenharmony_ci		pr_warn("siw: [QP %u]: rresp stag: %08x != %08x\n",
1928c2ecf20Sopenharmony_ci			qp_id(rx_qp(srx)), sink_stag, srx->ddp_stag);
1938c2ecf20Sopenharmony_ci		ecode = DDP_ECODE_T_INVALID_STAG;
1948c2ecf20Sopenharmony_ci		goto error;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci	if (unlikely(srx->ddp_to != sink_to)) {
1978c2ecf20Sopenharmony_ci		pr_warn("siw: [QP %u]: rresp off: %016llx != %016llx\n",
1988c2ecf20Sopenharmony_ci			qp_id(rx_qp(srx)), (unsigned long long)sink_to,
1998c2ecf20Sopenharmony_ci			(unsigned long long)srx->ddp_to);
2008c2ecf20Sopenharmony_ci		ecode = DDP_ECODE_T_BASE_BOUNDS;
2018c2ecf20Sopenharmony_ci		goto error;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci	if (unlikely(!frx->more_ddp_segs &&
2048c2ecf20Sopenharmony_ci		     (wqe->processed + srx->fpdu_part_rem != wqe->bytes))) {
2058c2ecf20Sopenharmony_ci		pr_warn("siw: [QP %u]: rresp len: %d != %d\n",
2068c2ecf20Sopenharmony_ci			qp_id(rx_qp(srx)),
2078c2ecf20Sopenharmony_ci			wqe->processed + srx->fpdu_part_rem, wqe->bytes);
2088c2ecf20Sopenharmony_ci		ecode = DDP_ECODE_T_BASE_BOUNDS;
2098c2ecf20Sopenharmony_ci		goto error;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci	return 0;
2128c2ecf20Sopenharmony_cierror:
2138c2ecf20Sopenharmony_ci	siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_DDP,
2148c2ecf20Sopenharmony_ci			   DDP_ETYPE_TAGGED_BUF, ecode, 0);
2158c2ecf20Sopenharmony_ci	return -EINVAL;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/*
2198c2ecf20Sopenharmony_ci * siw_write_check_ntoh()
2208c2ecf20Sopenharmony_ci *
2218c2ecf20Sopenharmony_ci * Check incoming WRITE fragment header against expected
2228c2ecf20Sopenharmony_ci * header values and update expected values for potential next
2238c2ecf20Sopenharmony_ci * fragment
2248c2ecf20Sopenharmony_ci *
2258c2ecf20Sopenharmony_ci * NOTE: This function must be called only if a WRITE DDP segment
2268c2ecf20Sopenharmony_ci *       starts but not for fragmented consecutive pieces of an
2278c2ecf20Sopenharmony_ci *       already started DDP segment.
2288c2ecf20Sopenharmony_ci */
2298c2ecf20Sopenharmony_cistatic int siw_write_check_ntoh(struct siw_rx_stream *srx,
2308c2ecf20Sopenharmony_ci				struct siw_rx_fpdu *frx)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct iwarp_rdma_write *write = &srx->hdr.rwrite;
2338c2ecf20Sopenharmony_ci	enum ddp_ecode ecode;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	u32 sink_stag = be32_to_cpu(write->sink_stag);
2368c2ecf20Sopenharmony_ci	u64 sink_to = be64_to_cpu(write->sink_to);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (frx->first_ddp_seg) {
2398c2ecf20Sopenharmony_ci		srx->ddp_stag = sink_stag;
2408c2ecf20Sopenharmony_ci		srx->ddp_to = sink_to;
2418c2ecf20Sopenharmony_ci		frx->pbl_idx = 0;
2428c2ecf20Sopenharmony_ci	} else {
2438c2ecf20Sopenharmony_ci		if (unlikely(srx->ddp_stag != sink_stag)) {
2448c2ecf20Sopenharmony_ci			pr_warn("siw: [QP %u]: write stag: %08x != %08x\n",
2458c2ecf20Sopenharmony_ci				qp_id(rx_qp(srx)), sink_stag,
2468c2ecf20Sopenharmony_ci				srx->ddp_stag);
2478c2ecf20Sopenharmony_ci			ecode = DDP_ECODE_T_INVALID_STAG;
2488c2ecf20Sopenharmony_ci			goto error;
2498c2ecf20Sopenharmony_ci		}
2508c2ecf20Sopenharmony_ci		if (unlikely(srx->ddp_to != sink_to)) {
2518c2ecf20Sopenharmony_ci			pr_warn("siw: [QP %u]: write off: %016llx != %016llx\n",
2528c2ecf20Sopenharmony_ci				qp_id(rx_qp(srx)),
2538c2ecf20Sopenharmony_ci				(unsigned long long)sink_to,
2548c2ecf20Sopenharmony_ci				(unsigned long long)srx->ddp_to);
2558c2ecf20Sopenharmony_ci			ecode = DDP_ECODE_T_BASE_BOUNDS;
2568c2ecf20Sopenharmony_ci			goto error;
2578c2ecf20Sopenharmony_ci		}
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci	return 0;
2608c2ecf20Sopenharmony_cierror:
2618c2ecf20Sopenharmony_ci	siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_DDP,
2628c2ecf20Sopenharmony_ci			   DDP_ETYPE_TAGGED_BUF, ecode, 0);
2638c2ecf20Sopenharmony_ci	return -EINVAL;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci/*
2678c2ecf20Sopenharmony_ci * siw_send_check_ntoh()
2688c2ecf20Sopenharmony_ci *
2698c2ecf20Sopenharmony_ci * Check incoming SEND fragment header against expected
2708c2ecf20Sopenharmony_ci * header values and update expected MSN if no next
2718c2ecf20Sopenharmony_ci * fragment expected
2728c2ecf20Sopenharmony_ci *
2738c2ecf20Sopenharmony_ci * NOTE: This function must be called only if a SEND DDP segment
2748c2ecf20Sopenharmony_ci *       starts but not for fragmented consecutive pieces of an
2758c2ecf20Sopenharmony_ci *       already started DDP segment.
2768c2ecf20Sopenharmony_ci */
2778c2ecf20Sopenharmony_cistatic int siw_send_check_ntoh(struct siw_rx_stream *srx,
2788c2ecf20Sopenharmony_ci			       struct siw_rx_fpdu *frx)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	struct iwarp_send_inv *send = &srx->hdr.send_inv;
2818c2ecf20Sopenharmony_ci	struct siw_wqe *wqe = &frx->wqe_active;
2828c2ecf20Sopenharmony_ci	enum ddp_ecode ecode;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	u32 ddp_msn = be32_to_cpu(send->ddp_msn);
2858c2ecf20Sopenharmony_ci	u32 ddp_mo = be32_to_cpu(send->ddp_mo);
2868c2ecf20Sopenharmony_ci	u32 ddp_qn = be32_to_cpu(send->ddp_qn);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (unlikely(ddp_qn != RDMAP_UNTAGGED_QN_SEND)) {
2898c2ecf20Sopenharmony_ci		pr_warn("siw: [QP %u]: invalid ddp qn %d for send\n",
2908c2ecf20Sopenharmony_ci			qp_id(rx_qp(srx)), ddp_qn);
2918c2ecf20Sopenharmony_ci		ecode = DDP_ECODE_UT_INVALID_QN;
2928c2ecf20Sopenharmony_ci		goto error;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci	if (unlikely(ddp_msn != srx->ddp_msn[RDMAP_UNTAGGED_QN_SEND])) {
2958c2ecf20Sopenharmony_ci		pr_warn("siw: [QP %u]: send msn: %u != %u\n",
2968c2ecf20Sopenharmony_ci			qp_id(rx_qp(srx)), ddp_msn,
2978c2ecf20Sopenharmony_ci			srx->ddp_msn[RDMAP_UNTAGGED_QN_SEND]);
2988c2ecf20Sopenharmony_ci		ecode = DDP_ECODE_UT_INVALID_MSN_RANGE;
2998c2ecf20Sopenharmony_ci		goto error;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci	if (unlikely(ddp_mo != wqe->processed)) {
3028c2ecf20Sopenharmony_ci		pr_warn("siw: [QP %u], send mo: %u != %u\n",
3038c2ecf20Sopenharmony_ci			qp_id(rx_qp(srx)), ddp_mo, wqe->processed);
3048c2ecf20Sopenharmony_ci		ecode = DDP_ECODE_UT_INVALID_MO;
3058c2ecf20Sopenharmony_ci		goto error;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci	if (frx->first_ddp_seg) {
3088c2ecf20Sopenharmony_ci		/* initialize user memory write position */
3098c2ecf20Sopenharmony_ci		frx->sge_idx = 0;
3108c2ecf20Sopenharmony_ci		frx->sge_off = 0;
3118c2ecf20Sopenharmony_ci		frx->pbl_idx = 0;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci		/* only valid for SEND_INV and SEND_SE_INV operations */
3148c2ecf20Sopenharmony_ci		srx->inval_stag = be32_to_cpu(send->inval_stag);
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci	if (unlikely(wqe->bytes < wqe->processed + srx->fpdu_part_rem)) {
3178c2ecf20Sopenharmony_ci		siw_dbg_qp(rx_qp(srx), "receive space short: %d - %d < %d\n",
3188c2ecf20Sopenharmony_ci			   wqe->bytes, wqe->processed, srx->fpdu_part_rem);
3198c2ecf20Sopenharmony_ci		wqe->wc_status = SIW_WC_LOC_LEN_ERR;
3208c2ecf20Sopenharmony_ci		ecode = DDP_ECODE_UT_INVALID_MSN_NOBUF;
3218c2ecf20Sopenharmony_ci		goto error;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci	return 0;
3248c2ecf20Sopenharmony_cierror:
3258c2ecf20Sopenharmony_ci	siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_DDP,
3268c2ecf20Sopenharmony_ci			   DDP_ETYPE_UNTAGGED_BUF, ecode, 0);
3278c2ecf20Sopenharmony_ci	return -EINVAL;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic struct siw_wqe *siw_rqe_get(struct siw_qp *qp)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct siw_rqe *rqe;
3338c2ecf20Sopenharmony_ci	struct siw_srq *srq;
3348c2ecf20Sopenharmony_ci	struct siw_wqe *wqe = NULL;
3358c2ecf20Sopenharmony_ci	bool srq_event = false;
3368c2ecf20Sopenharmony_ci	unsigned long flags;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	srq = qp->srq;
3398c2ecf20Sopenharmony_ci	if (srq) {
3408c2ecf20Sopenharmony_ci		spin_lock_irqsave(&srq->lock, flags);
3418c2ecf20Sopenharmony_ci		if (unlikely(!srq->num_rqe))
3428c2ecf20Sopenharmony_ci			goto out;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci		rqe = &srq->recvq[srq->rq_get % srq->num_rqe];
3458c2ecf20Sopenharmony_ci	} else {
3468c2ecf20Sopenharmony_ci		if (unlikely(!qp->recvq))
3478c2ecf20Sopenharmony_ci			goto out;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci		rqe = &qp->recvq[qp->rq_get % qp->attrs.rq_size];
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci	if (likely(rqe->flags == SIW_WQE_VALID)) {
3528c2ecf20Sopenharmony_ci		int num_sge = rqe->num_sge;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		if (likely(num_sge <= SIW_MAX_SGE)) {
3558c2ecf20Sopenharmony_ci			int i = 0;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci			wqe = rx_wqe(&qp->rx_untagged);
3588c2ecf20Sopenharmony_ci			rx_type(wqe) = SIW_OP_RECEIVE;
3598c2ecf20Sopenharmony_ci			wqe->wr_status = SIW_WR_INPROGRESS;
3608c2ecf20Sopenharmony_ci			wqe->bytes = 0;
3618c2ecf20Sopenharmony_ci			wqe->processed = 0;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci			wqe->rqe.id = rqe->id;
3648c2ecf20Sopenharmony_ci			wqe->rqe.num_sge = num_sge;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci			while (i < num_sge) {
3678c2ecf20Sopenharmony_ci				wqe->rqe.sge[i].laddr = rqe->sge[i].laddr;
3688c2ecf20Sopenharmony_ci				wqe->rqe.sge[i].lkey = rqe->sge[i].lkey;
3698c2ecf20Sopenharmony_ci				wqe->rqe.sge[i].length = rqe->sge[i].length;
3708c2ecf20Sopenharmony_ci				wqe->bytes += wqe->rqe.sge[i].length;
3718c2ecf20Sopenharmony_ci				wqe->mem[i] = NULL;
3728c2ecf20Sopenharmony_ci				i++;
3738c2ecf20Sopenharmony_ci			}
3748c2ecf20Sopenharmony_ci			/* can be re-used by appl */
3758c2ecf20Sopenharmony_ci			smp_store_mb(rqe->flags, 0);
3768c2ecf20Sopenharmony_ci		} else {
3778c2ecf20Sopenharmony_ci			siw_dbg_qp(qp, "too many sge's: %d\n", rqe->num_sge);
3788c2ecf20Sopenharmony_ci			if (srq)
3798c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&srq->lock, flags);
3808c2ecf20Sopenharmony_ci			return NULL;
3818c2ecf20Sopenharmony_ci		}
3828c2ecf20Sopenharmony_ci		if (!srq) {
3838c2ecf20Sopenharmony_ci			qp->rq_get++;
3848c2ecf20Sopenharmony_ci		} else {
3858c2ecf20Sopenharmony_ci			if (srq->armed) {
3868c2ecf20Sopenharmony_ci				/* Test SRQ limit */
3878c2ecf20Sopenharmony_ci				u32 off = (srq->rq_get + srq->limit) %
3888c2ecf20Sopenharmony_ci					  srq->num_rqe;
3898c2ecf20Sopenharmony_ci				struct siw_rqe *rqe2 = &srq->recvq[off];
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci				if (!(rqe2->flags & SIW_WQE_VALID)) {
3928c2ecf20Sopenharmony_ci					srq->armed = false;
3938c2ecf20Sopenharmony_ci					srq_event = true;
3948c2ecf20Sopenharmony_ci				}
3958c2ecf20Sopenharmony_ci			}
3968c2ecf20Sopenharmony_ci			srq->rq_get++;
3978c2ecf20Sopenharmony_ci		}
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ciout:
4008c2ecf20Sopenharmony_ci	if (srq) {
4018c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&srq->lock, flags);
4028c2ecf20Sopenharmony_ci		if (srq_event)
4038c2ecf20Sopenharmony_ci			siw_srq_event(srq, IB_EVENT_SRQ_LIMIT_REACHED);
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci	return wqe;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci/*
4098c2ecf20Sopenharmony_ci * siw_proc_send:
4108c2ecf20Sopenharmony_ci *
4118c2ecf20Sopenharmony_ci * Process one incoming SEND and place data into memory referenced by
4128c2ecf20Sopenharmony_ci * receive wqe.
4138c2ecf20Sopenharmony_ci *
4148c2ecf20Sopenharmony_ci * Function supports partially received sends (suspending/resuming
4158c2ecf20Sopenharmony_ci * current receive wqe processing)
4168c2ecf20Sopenharmony_ci *
4178c2ecf20Sopenharmony_ci * return value:
4188c2ecf20Sopenharmony_ci *	0:       reached the end of a DDP segment
4198c2ecf20Sopenharmony_ci *	-EAGAIN: to be called again to finish the DDP segment
4208c2ecf20Sopenharmony_ci */
4218c2ecf20Sopenharmony_ciint siw_proc_send(struct siw_qp *qp)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
4248c2ecf20Sopenharmony_ci	struct siw_rx_fpdu *frx = &qp->rx_untagged;
4258c2ecf20Sopenharmony_ci	struct siw_wqe *wqe;
4268c2ecf20Sopenharmony_ci	u32 data_bytes; /* all data bytes available */
4278c2ecf20Sopenharmony_ci	u32 rcvd_bytes; /* sum of data bytes rcvd */
4288c2ecf20Sopenharmony_ci	int rv = 0;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if (frx->first_ddp_seg) {
4318c2ecf20Sopenharmony_ci		wqe = siw_rqe_get(qp);
4328c2ecf20Sopenharmony_ci		if (unlikely(!wqe)) {
4338c2ecf20Sopenharmony_ci			siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
4348c2ecf20Sopenharmony_ci					   DDP_ETYPE_UNTAGGED_BUF,
4358c2ecf20Sopenharmony_ci					   DDP_ECODE_UT_INVALID_MSN_NOBUF, 0);
4368c2ecf20Sopenharmony_ci			return -ENOENT;
4378c2ecf20Sopenharmony_ci		}
4388c2ecf20Sopenharmony_ci	} else {
4398c2ecf20Sopenharmony_ci		wqe = rx_wqe(frx);
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci	if (srx->state == SIW_GET_DATA_START) {
4428c2ecf20Sopenharmony_ci		rv = siw_send_check_ntoh(srx, frx);
4438c2ecf20Sopenharmony_ci		if (unlikely(rv)) {
4448c2ecf20Sopenharmony_ci			siw_qp_event(qp, IB_EVENT_QP_FATAL);
4458c2ecf20Sopenharmony_ci			return rv;
4468c2ecf20Sopenharmony_ci		}
4478c2ecf20Sopenharmony_ci		if (!srx->fpdu_part_rem) /* zero length SEND */
4488c2ecf20Sopenharmony_ci			return 0;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci	data_bytes = min(srx->fpdu_part_rem, srx->skb_new);
4518c2ecf20Sopenharmony_ci	rcvd_bytes = 0;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* A zero length SEND will skip below loop */
4548c2ecf20Sopenharmony_ci	while (data_bytes) {
4558c2ecf20Sopenharmony_ci		struct ib_pd *pd;
4568c2ecf20Sopenharmony_ci		struct siw_mem **mem, *mem_p;
4578c2ecf20Sopenharmony_ci		struct siw_sge *sge;
4588c2ecf20Sopenharmony_ci		u32 sge_bytes; /* data bytes avail for SGE */
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		sge = &wqe->rqe.sge[frx->sge_idx];
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		if (!sge->length) {
4638c2ecf20Sopenharmony_ci			/* just skip empty sge's */
4648c2ecf20Sopenharmony_ci			frx->sge_idx++;
4658c2ecf20Sopenharmony_ci			frx->sge_off = 0;
4668c2ecf20Sopenharmony_ci			frx->pbl_idx = 0;
4678c2ecf20Sopenharmony_ci			continue;
4688c2ecf20Sopenharmony_ci		}
4698c2ecf20Sopenharmony_ci		sge_bytes = min(data_bytes, sge->length - frx->sge_off);
4708c2ecf20Sopenharmony_ci		mem = &wqe->mem[frx->sge_idx];
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci		/*
4738c2ecf20Sopenharmony_ci		 * check with QP's PD if no SRQ present, SRQ's PD otherwise
4748c2ecf20Sopenharmony_ci		 */
4758c2ecf20Sopenharmony_ci		pd = qp->srq == NULL ? qp->pd : qp->srq->base_srq.pd;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci		rv = siw_check_sge(pd, sge, mem, IB_ACCESS_LOCAL_WRITE,
4788c2ecf20Sopenharmony_ci				   frx->sge_off, sge_bytes);
4798c2ecf20Sopenharmony_ci		if (unlikely(rv)) {
4808c2ecf20Sopenharmony_ci			siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
4818c2ecf20Sopenharmony_ci					   DDP_ETYPE_CATASTROPHIC,
4828c2ecf20Sopenharmony_ci					   DDP_ECODE_CATASTROPHIC, 0);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci			siw_qp_event(qp, IB_EVENT_QP_ACCESS_ERR);
4858c2ecf20Sopenharmony_ci			break;
4868c2ecf20Sopenharmony_ci		}
4878c2ecf20Sopenharmony_ci		mem_p = *mem;
4888c2ecf20Sopenharmony_ci		if (mem_p->mem_obj == NULL)
4898c2ecf20Sopenharmony_ci			rv = siw_rx_kva(srx,
4908c2ecf20Sopenharmony_ci				(void *)(uintptr_t)(sge->laddr + frx->sge_off),
4918c2ecf20Sopenharmony_ci				sge_bytes);
4928c2ecf20Sopenharmony_ci		else if (!mem_p->is_pbl)
4938c2ecf20Sopenharmony_ci			rv = siw_rx_umem(srx, mem_p->umem,
4948c2ecf20Sopenharmony_ci					 sge->laddr + frx->sge_off, sge_bytes);
4958c2ecf20Sopenharmony_ci		else
4968c2ecf20Sopenharmony_ci			rv = siw_rx_pbl(srx, &frx->pbl_idx, mem_p,
4978c2ecf20Sopenharmony_ci					sge->laddr + frx->sge_off, sge_bytes);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		if (unlikely(rv != sge_bytes)) {
5008c2ecf20Sopenharmony_ci			wqe->processed += rcvd_bytes;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci			siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
5038c2ecf20Sopenharmony_ci					   DDP_ETYPE_CATASTROPHIC,
5048c2ecf20Sopenharmony_ci					   DDP_ECODE_CATASTROPHIC, 0);
5058c2ecf20Sopenharmony_ci			return -EINVAL;
5068c2ecf20Sopenharmony_ci		}
5078c2ecf20Sopenharmony_ci		frx->sge_off += rv;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci		if (frx->sge_off == sge->length) {
5108c2ecf20Sopenharmony_ci			frx->sge_idx++;
5118c2ecf20Sopenharmony_ci			frx->sge_off = 0;
5128c2ecf20Sopenharmony_ci			frx->pbl_idx = 0;
5138c2ecf20Sopenharmony_ci		}
5148c2ecf20Sopenharmony_ci		data_bytes -= rv;
5158c2ecf20Sopenharmony_ci		rcvd_bytes += rv;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		srx->fpdu_part_rem -= rv;
5188c2ecf20Sopenharmony_ci		srx->fpdu_part_rcvd += rv;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci	wqe->processed += rcvd_bytes;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	if (!srx->fpdu_part_rem)
5238c2ecf20Sopenharmony_ci		return 0;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	return (rv < 0) ? rv : -EAGAIN;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci/*
5298c2ecf20Sopenharmony_ci * siw_proc_write:
5308c2ecf20Sopenharmony_ci *
5318c2ecf20Sopenharmony_ci * Place incoming WRITE after referencing and checking target buffer
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci * Function supports partially received WRITEs (suspending/resuming
5348c2ecf20Sopenharmony_ci * current receive processing)
5358c2ecf20Sopenharmony_ci *
5368c2ecf20Sopenharmony_ci * return value:
5378c2ecf20Sopenharmony_ci *	0:       reached the end of a DDP segment
5388c2ecf20Sopenharmony_ci *	-EAGAIN: to be called again to finish the DDP segment
5398c2ecf20Sopenharmony_ci */
5408c2ecf20Sopenharmony_ciint siw_proc_write(struct siw_qp *qp)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
5438c2ecf20Sopenharmony_ci	struct siw_rx_fpdu *frx = &qp->rx_tagged;
5448c2ecf20Sopenharmony_ci	struct siw_mem *mem;
5458c2ecf20Sopenharmony_ci	int bytes, rv;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	if (srx->state == SIW_GET_DATA_START) {
5488c2ecf20Sopenharmony_ci		if (!srx->fpdu_part_rem) /* zero length WRITE */
5498c2ecf20Sopenharmony_ci			return 0;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci		rv = siw_write_check_ntoh(srx, frx);
5528c2ecf20Sopenharmony_ci		if (unlikely(rv)) {
5538c2ecf20Sopenharmony_ci			siw_qp_event(qp, IB_EVENT_QP_FATAL);
5548c2ecf20Sopenharmony_ci			return rv;
5558c2ecf20Sopenharmony_ci		}
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci	bytes = min(srx->fpdu_part_rem, srx->skb_new);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	if (frx->first_ddp_seg) {
5608c2ecf20Sopenharmony_ci		struct siw_wqe *wqe = rx_wqe(frx);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci		rx_mem(frx) = siw_mem_id2obj(qp->sdev, srx->ddp_stag >> 8);
5638c2ecf20Sopenharmony_ci		if (unlikely(!rx_mem(frx))) {
5648c2ecf20Sopenharmony_ci			siw_dbg_qp(qp,
5658c2ecf20Sopenharmony_ci				   "sink stag not found/invalid, stag 0x%08x\n",
5668c2ecf20Sopenharmony_ci				   srx->ddp_stag);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci			siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
5698c2ecf20Sopenharmony_ci					   DDP_ETYPE_TAGGED_BUF,
5708c2ecf20Sopenharmony_ci					   DDP_ECODE_T_INVALID_STAG, 0);
5718c2ecf20Sopenharmony_ci			return -EINVAL;
5728c2ecf20Sopenharmony_ci		}
5738c2ecf20Sopenharmony_ci		wqe->rqe.num_sge = 1;
5748c2ecf20Sopenharmony_ci		rx_type(wqe) = SIW_OP_WRITE;
5758c2ecf20Sopenharmony_ci		wqe->wr_status = SIW_WR_INPROGRESS;
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci	mem = rx_mem(frx);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	/*
5808c2ecf20Sopenharmony_ci	 * Check if application re-registered memory with different
5818c2ecf20Sopenharmony_ci	 * key field of STag.
5828c2ecf20Sopenharmony_ci	 */
5838c2ecf20Sopenharmony_ci	if (unlikely(mem->stag != srx->ddp_stag)) {
5848c2ecf20Sopenharmony_ci		siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
5858c2ecf20Sopenharmony_ci				   DDP_ETYPE_TAGGED_BUF,
5868c2ecf20Sopenharmony_ci				   DDP_ECODE_T_INVALID_STAG, 0);
5878c2ecf20Sopenharmony_ci		return -EINVAL;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci	rv = siw_check_mem(qp->pd, mem, srx->ddp_to + srx->fpdu_part_rcvd,
5908c2ecf20Sopenharmony_ci			   IB_ACCESS_REMOTE_WRITE, bytes);
5918c2ecf20Sopenharmony_ci	if (unlikely(rv)) {
5928c2ecf20Sopenharmony_ci		siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
5938c2ecf20Sopenharmony_ci				   DDP_ETYPE_TAGGED_BUF, siw_tagged_error(-rv),
5948c2ecf20Sopenharmony_ci				   0);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci		siw_qp_event(qp, IB_EVENT_QP_ACCESS_ERR);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci		return -EINVAL;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (mem->mem_obj == NULL)
6028c2ecf20Sopenharmony_ci		rv = siw_rx_kva(srx,
6038c2ecf20Sopenharmony_ci			(void *)(uintptr_t)(srx->ddp_to + srx->fpdu_part_rcvd),
6048c2ecf20Sopenharmony_ci			bytes);
6058c2ecf20Sopenharmony_ci	else if (!mem->is_pbl)
6068c2ecf20Sopenharmony_ci		rv = siw_rx_umem(srx, mem->umem,
6078c2ecf20Sopenharmony_ci				 srx->ddp_to + srx->fpdu_part_rcvd, bytes);
6088c2ecf20Sopenharmony_ci	else
6098c2ecf20Sopenharmony_ci		rv = siw_rx_pbl(srx, &frx->pbl_idx, mem,
6108c2ecf20Sopenharmony_ci				srx->ddp_to + srx->fpdu_part_rcvd, bytes);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (unlikely(rv != bytes)) {
6138c2ecf20Sopenharmony_ci		siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
6148c2ecf20Sopenharmony_ci				   DDP_ETYPE_CATASTROPHIC,
6158c2ecf20Sopenharmony_ci				   DDP_ECODE_CATASTROPHIC, 0);
6168c2ecf20Sopenharmony_ci		return -EINVAL;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci	srx->fpdu_part_rem -= rv;
6198c2ecf20Sopenharmony_ci	srx->fpdu_part_rcvd += rv;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	if (!srx->fpdu_part_rem) {
6228c2ecf20Sopenharmony_ci		srx->ddp_to += srx->fpdu_part_rcvd;
6238c2ecf20Sopenharmony_ci		return 0;
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci	return -EAGAIN;
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci/*
6298c2ecf20Sopenharmony_ci * Inbound RREQ's cannot carry user data.
6308c2ecf20Sopenharmony_ci */
6318c2ecf20Sopenharmony_ciint siw_proc_rreq(struct siw_qp *qp)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (!srx->fpdu_part_rem)
6368c2ecf20Sopenharmony_ci		return 0;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	pr_warn("siw: [QP %u]: rreq with mpa len %d\n", qp_id(qp),
6398c2ecf20Sopenharmony_ci		be16_to_cpu(srx->hdr.ctrl.mpa_len));
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	return -EPROTO;
6428c2ecf20Sopenharmony_ci}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci/*
6458c2ecf20Sopenharmony_ci * siw_init_rresp:
6468c2ecf20Sopenharmony_ci *
6478c2ecf20Sopenharmony_ci * Process inbound RDMA READ REQ. Produce a pseudo READ RESPONSE WQE.
6488c2ecf20Sopenharmony_ci * Put it at the tail of the IRQ, if there is another WQE currently in
6498c2ecf20Sopenharmony_ci * transmit processing. If not, make it the current WQE to be processed
6508c2ecf20Sopenharmony_ci * and schedule transmit processing.
6518c2ecf20Sopenharmony_ci *
6528c2ecf20Sopenharmony_ci * Can be called from softirq context and from process
6538c2ecf20Sopenharmony_ci * context (RREAD socket loopback case!)
6548c2ecf20Sopenharmony_ci *
6558c2ecf20Sopenharmony_ci * return value:
6568c2ecf20Sopenharmony_ci *	0:      success,
6578c2ecf20Sopenharmony_ci *		failure code otherwise
6588c2ecf20Sopenharmony_ci */
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic int siw_init_rresp(struct siw_qp *qp, struct siw_rx_stream *srx)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	struct siw_wqe *tx_work = tx_wqe(qp);
6638c2ecf20Sopenharmony_ci	struct siw_sqe *resp;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	uint64_t raddr = be64_to_cpu(srx->hdr.rreq.sink_to),
6668c2ecf20Sopenharmony_ci		 laddr = be64_to_cpu(srx->hdr.rreq.source_to);
6678c2ecf20Sopenharmony_ci	uint32_t length = be32_to_cpu(srx->hdr.rreq.read_size),
6688c2ecf20Sopenharmony_ci		 lkey = be32_to_cpu(srx->hdr.rreq.source_stag),
6698c2ecf20Sopenharmony_ci		 rkey = be32_to_cpu(srx->hdr.rreq.sink_stag),
6708c2ecf20Sopenharmony_ci		 msn = be32_to_cpu(srx->hdr.rreq.ddp_msn);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	int run_sq = 1, rv = 0;
6738c2ecf20Sopenharmony_ci	unsigned long flags;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if (unlikely(msn != srx->ddp_msn[RDMAP_UNTAGGED_QN_RDMA_READ])) {
6768c2ecf20Sopenharmony_ci		siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
6778c2ecf20Sopenharmony_ci				   DDP_ETYPE_UNTAGGED_BUF,
6788c2ecf20Sopenharmony_ci				   DDP_ECODE_UT_INVALID_MSN_RANGE, 0);
6798c2ecf20Sopenharmony_ci		return -EPROTO;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci	spin_lock_irqsave(&qp->sq_lock, flags);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (unlikely(!qp->attrs.irq_size)) {
6848c2ecf20Sopenharmony_ci		run_sq = 0;
6858c2ecf20Sopenharmony_ci		goto error_irq;
6868c2ecf20Sopenharmony_ci	}
6878c2ecf20Sopenharmony_ci	if (tx_work->wr_status == SIW_WR_IDLE) {
6888c2ecf20Sopenharmony_ci		/*
6898c2ecf20Sopenharmony_ci		 * immediately schedule READ response w/o
6908c2ecf20Sopenharmony_ci		 * consuming IRQ entry: IRQ must be empty.
6918c2ecf20Sopenharmony_ci		 */
6928c2ecf20Sopenharmony_ci		tx_work->processed = 0;
6938c2ecf20Sopenharmony_ci		tx_work->mem[0] = NULL;
6948c2ecf20Sopenharmony_ci		tx_work->wr_status = SIW_WR_QUEUED;
6958c2ecf20Sopenharmony_ci		resp = &tx_work->sqe;
6968c2ecf20Sopenharmony_ci	} else {
6978c2ecf20Sopenharmony_ci		resp = irq_alloc_free(qp);
6988c2ecf20Sopenharmony_ci		run_sq = 0;
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci	if (likely(resp)) {
7018c2ecf20Sopenharmony_ci		resp->opcode = SIW_OP_READ_RESPONSE;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci		resp->sge[0].length = length;
7048c2ecf20Sopenharmony_ci		resp->sge[0].laddr = laddr;
7058c2ecf20Sopenharmony_ci		resp->sge[0].lkey = lkey;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci		/* Keep aside message sequence number for potential
7088c2ecf20Sopenharmony_ci		 * error reporting during Read Response generation.
7098c2ecf20Sopenharmony_ci		 */
7108c2ecf20Sopenharmony_ci		resp->sge[1].length = msn;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci		resp->raddr = raddr;
7138c2ecf20Sopenharmony_ci		resp->rkey = rkey;
7148c2ecf20Sopenharmony_ci		resp->num_sge = length ? 1 : 0;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci		/* RRESP now valid as current TX wqe or placed into IRQ */
7178c2ecf20Sopenharmony_ci		smp_store_mb(resp->flags, SIW_WQE_VALID);
7188c2ecf20Sopenharmony_ci	} else {
7198c2ecf20Sopenharmony_cierror_irq:
7208c2ecf20Sopenharmony_ci		pr_warn("siw: [QP %u]: IRQ exceeded or null, size %d\n",
7218c2ecf20Sopenharmony_ci			qp_id(qp), qp->attrs.irq_size);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci		siw_init_terminate(qp, TERM_ERROR_LAYER_RDMAP,
7248c2ecf20Sopenharmony_ci				   RDMAP_ETYPE_REMOTE_OPERATION,
7258c2ecf20Sopenharmony_ci				   RDMAP_ECODE_CATASTROPHIC_STREAM, 0);
7268c2ecf20Sopenharmony_ci		rv = -EPROTO;
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&qp->sq_lock, flags);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	if (run_sq)
7328c2ecf20Sopenharmony_ci		rv = siw_sq_start(qp);
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	return rv;
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci/*
7388c2ecf20Sopenharmony_ci * Only called at start of Read.Resonse processing.
7398c2ecf20Sopenharmony_ci * Transfer pending Read from tip of ORQ into currrent rx wqe,
7408c2ecf20Sopenharmony_ci * but keep ORQ entry valid until Read.Response processing done.
7418c2ecf20Sopenharmony_ci * No Queue locking needed.
7428c2ecf20Sopenharmony_ci */
7438c2ecf20Sopenharmony_cistatic int siw_orqe_start_rx(struct siw_qp *qp)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	struct siw_sqe *orqe;
7468c2ecf20Sopenharmony_ci	struct siw_wqe *wqe = NULL;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	if (unlikely(!qp->attrs.orq_size))
7498c2ecf20Sopenharmony_ci		return -EPROTO;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	/* make sure ORQ indices are current */
7528c2ecf20Sopenharmony_ci	smp_mb();
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	orqe = orq_get_current(qp);
7558c2ecf20Sopenharmony_ci	if (READ_ONCE(orqe->flags) & SIW_WQE_VALID) {
7568c2ecf20Sopenharmony_ci		/* RRESP is a TAGGED RDMAP operation */
7578c2ecf20Sopenharmony_ci		wqe = rx_wqe(&qp->rx_tagged);
7588c2ecf20Sopenharmony_ci		wqe->sqe.id = orqe->id;
7598c2ecf20Sopenharmony_ci		wqe->sqe.opcode = orqe->opcode;
7608c2ecf20Sopenharmony_ci		wqe->sqe.sge[0].laddr = orqe->sge[0].laddr;
7618c2ecf20Sopenharmony_ci		wqe->sqe.sge[0].lkey = orqe->sge[0].lkey;
7628c2ecf20Sopenharmony_ci		wqe->sqe.sge[0].length = orqe->sge[0].length;
7638c2ecf20Sopenharmony_ci		wqe->sqe.flags = orqe->flags;
7648c2ecf20Sopenharmony_ci		wqe->sqe.num_sge = 1;
7658c2ecf20Sopenharmony_ci		wqe->bytes = orqe->sge[0].length;
7668c2ecf20Sopenharmony_ci		wqe->processed = 0;
7678c2ecf20Sopenharmony_ci		wqe->mem[0] = NULL;
7688c2ecf20Sopenharmony_ci		/* make sure WQE is completely written before valid */
7698c2ecf20Sopenharmony_ci		smp_wmb();
7708c2ecf20Sopenharmony_ci		wqe->wr_status = SIW_WR_INPROGRESS;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci		return 0;
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci	return -EPROTO;
7758c2ecf20Sopenharmony_ci}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci/*
7788c2ecf20Sopenharmony_ci * siw_proc_rresp:
7798c2ecf20Sopenharmony_ci *
7808c2ecf20Sopenharmony_ci * Place incoming RRESP data into memory referenced by RREQ WQE
7818c2ecf20Sopenharmony_ci * which is at the tip of the ORQ
7828c2ecf20Sopenharmony_ci *
7838c2ecf20Sopenharmony_ci * Function supports partially received RRESP's (suspending/resuming
7848c2ecf20Sopenharmony_ci * current receive processing)
7858c2ecf20Sopenharmony_ci */
7868c2ecf20Sopenharmony_ciint siw_proc_rresp(struct siw_qp *qp)
7878c2ecf20Sopenharmony_ci{
7888c2ecf20Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
7898c2ecf20Sopenharmony_ci	struct siw_rx_fpdu *frx = &qp->rx_tagged;
7908c2ecf20Sopenharmony_ci	struct siw_wqe *wqe = rx_wqe(frx);
7918c2ecf20Sopenharmony_ci	struct siw_mem **mem, *mem_p;
7928c2ecf20Sopenharmony_ci	struct siw_sge *sge;
7938c2ecf20Sopenharmony_ci	int bytes, rv;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	if (frx->first_ddp_seg) {
7968c2ecf20Sopenharmony_ci		if (unlikely(wqe->wr_status != SIW_WR_IDLE)) {
7978c2ecf20Sopenharmony_ci			pr_warn("siw: [QP %u]: proc RRESP: status %d, op %d\n",
7988c2ecf20Sopenharmony_ci				qp_id(qp), wqe->wr_status, wqe->sqe.opcode);
7998c2ecf20Sopenharmony_ci			rv = -EPROTO;
8008c2ecf20Sopenharmony_ci			goto error_term;
8018c2ecf20Sopenharmony_ci		}
8028c2ecf20Sopenharmony_ci		/*
8038c2ecf20Sopenharmony_ci		 * fetch pending RREQ from orq
8048c2ecf20Sopenharmony_ci		 */
8058c2ecf20Sopenharmony_ci		rv = siw_orqe_start_rx(qp);
8068c2ecf20Sopenharmony_ci		if (rv) {
8078c2ecf20Sopenharmony_ci			pr_warn("siw: [QP %u]: ORQ empty, size %d\n",
8088c2ecf20Sopenharmony_ci				qp_id(qp), qp->attrs.orq_size);
8098c2ecf20Sopenharmony_ci			goto error_term;
8108c2ecf20Sopenharmony_ci		}
8118c2ecf20Sopenharmony_ci		rv = siw_rresp_check_ntoh(srx, frx);
8128c2ecf20Sopenharmony_ci		if (unlikely(rv)) {
8138c2ecf20Sopenharmony_ci			siw_qp_event(qp, IB_EVENT_QP_FATAL);
8148c2ecf20Sopenharmony_ci			return rv;
8158c2ecf20Sopenharmony_ci		}
8168c2ecf20Sopenharmony_ci	} else {
8178c2ecf20Sopenharmony_ci		if (unlikely(wqe->wr_status != SIW_WR_INPROGRESS)) {
8188c2ecf20Sopenharmony_ci			pr_warn("siw: [QP %u]: resume RRESP: status %d\n",
8198c2ecf20Sopenharmony_ci				qp_id(qp), wqe->wr_status);
8208c2ecf20Sopenharmony_ci			rv = -EPROTO;
8218c2ecf20Sopenharmony_ci			goto error_term;
8228c2ecf20Sopenharmony_ci		}
8238c2ecf20Sopenharmony_ci	}
8248c2ecf20Sopenharmony_ci	if (!srx->fpdu_part_rem) /* zero length RRESPONSE */
8258c2ecf20Sopenharmony_ci		return 0;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	sge = wqe->sqe.sge; /* there is only one */
8288c2ecf20Sopenharmony_ci	mem = &wqe->mem[0];
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (!(*mem)) {
8318c2ecf20Sopenharmony_ci		/*
8328c2ecf20Sopenharmony_ci		 * check target memory which resolves memory on first fragment
8338c2ecf20Sopenharmony_ci		 */
8348c2ecf20Sopenharmony_ci		rv = siw_check_sge(qp->pd, sge, mem, IB_ACCESS_LOCAL_WRITE, 0,
8358c2ecf20Sopenharmony_ci				   wqe->bytes);
8368c2ecf20Sopenharmony_ci		if (unlikely(rv)) {
8378c2ecf20Sopenharmony_ci			siw_dbg_qp(qp, "target mem check: %d\n", rv);
8388c2ecf20Sopenharmony_ci			wqe->wc_status = SIW_WC_LOC_PROT_ERR;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci			siw_init_terminate(qp, TERM_ERROR_LAYER_DDP,
8418c2ecf20Sopenharmony_ci					   DDP_ETYPE_TAGGED_BUF,
8428c2ecf20Sopenharmony_ci					   siw_tagged_error(-rv), 0);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci			siw_qp_event(qp, IB_EVENT_QP_ACCESS_ERR);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci			return -EINVAL;
8478c2ecf20Sopenharmony_ci		}
8488c2ecf20Sopenharmony_ci	}
8498c2ecf20Sopenharmony_ci	mem_p = *mem;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	bytes = min(srx->fpdu_part_rem, srx->skb_new);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	if (mem_p->mem_obj == NULL)
8548c2ecf20Sopenharmony_ci		rv = siw_rx_kva(srx,
8558c2ecf20Sopenharmony_ci			(void *)(uintptr_t)(sge->laddr + wqe->processed),
8568c2ecf20Sopenharmony_ci			bytes);
8578c2ecf20Sopenharmony_ci	else if (!mem_p->is_pbl)
8588c2ecf20Sopenharmony_ci		rv = siw_rx_umem(srx, mem_p->umem, sge->laddr + wqe->processed,
8598c2ecf20Sopenharmony_ci				 bytes);
8608c2ecf20Sopenharmony_ci	else
8618c2ecf20Sopenharmony_ci		rv = siw_rx_pbl(srx, &frx->pbl_idx, mem_p,
8628c2ecf20Sopenharmony_ci				sge->laddr + wqe->processed, bytes);
8638c2ecf20Sopenharmony_ci	if (rv != bytes) {
8648c2ecf20Sopenharmony_ci		wqe->wc_status = SIW_WC_GENERAL_ERR;
8658c2ecf20Sopenharmony_ci		rv = -EINVAL;
8668c2ecf20Sopenharmony_ci		goto error_term;
8678c2ecf20Sopenharmony_ci	}
8688c2ecf20Sopenharmony_ci	srx->fpdu_part_rem -= rv;
8698c2ecf20Sopenharmony_ci	srx->fpdu_part_rcvd += rv;
8708c2ecf20Sopenharmony_ci	wqe->processed += rv;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	if (!srx->fpdu_part_rem) {
8738c2ecf20Sopenharmony_ci		srx->ddp_to += srx->fpdu_part_rcvd;
8748c2ecf20Sopenharmony_ci		return 0;
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci	return -EAGAIN;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_cierror_term:
8798c2ecf20Sopenharmony_ci	siw_init_terminate(qp, TERM_ERROR_LAYER_DDP, DDP_ETYPE_CATASTROPHIC,
8808c2ecf20Sopenharmony_ci			   DDP_ECODE_CATASTROPHIC, 0);
8818c2ecf20Sopenharmony_ci	return rv;
8828c2ecf20Sopenharmony_ci}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ciint siw_proc_terminate(struct siw_qp *qp)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
8878c2ecf20Sopenharmony_ci	struct sk_buff *skb = srx->skb;
8888c2ecf20Sopenharmony_ci	struct iwarp_terminate *term = &srx->hdr.terminate;
8898c2ecf20Sopenharmony_ci	union iwarp_hdr term_info;
8908c2ecf20Sopenharmony_ci	u8 *infop = (u8 *)&term_info;
8918c2ecf20Sopenharmony_ci	enum rdma_opcode op;
8928c2ecf20Sopenharmony_ci	u16 to_copy = sizeof(struct iwarp_ctrl);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	pr_warn("siw: got TERMINATE. layer %d, type %d, code %d\n",
8958c2ecf20Sopenharmony_ci		__rdmap_term_layer(term), __rdmap_term_etype(term),
8968c2ecf20Sopenharmony_ci		__rdmap_term_ecode(term));
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	if (be32_to_cpu(term->ddp_qn) != RDMAP_UNTAGGED_QN_TERMINATE ||
8998c2ecf20Sopenharmony_ci	    be32_to_cpu(term->ddp_msn) !=
9008c2ecf20Sopenharmony_ci		    qp->rx_stream.ddp_msn[RDMAP_UNTAGGED_QN_TERMINATE] ||
9018c2ecf20Sopenharmony_ci	    be32_to_cpu(term->ddp_mo) != 0) {
9028c2ecf20Sopenharmony_ci		pr_warn("siw: rx bogus TERM [QN x%08x, MSN x%08x, MO x%08x]\n",
9038c2ecf20Sopenharmony_ci			be32_to_cpu(term->ddp_qn), be32_to_cpu(term->ddp_msn),
9048c2ecf20Sopenharmony_ci			be32_to_cpu(term->ddp_mo));
9058c2ecf20Sopenharmony_ci		return -ECONNRESET;
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci	/*
9088c2ecf20Sopenharmony_ci	 * Receive remaining pieces of TERM if indicated
9098c2ecf20Sopenharmony_ci	 */
9108c2ecf20Sopenharmony_ci	if (!term->flag_m)
9118c2ecf20Sopenharmony_ci		return -ECONNRESET;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	/* Do not take the effort to reassemble a network fragmented
9148c2ecf20Sopenharmony_ci	 * TERM message
9158c2ecf20Sopenharmony_ci	 */
9168c2ecf20Sopenharmony_ci	if (srx->skb_new < sizeof(struct iwarp_ctrl_tagged))
9178c2ecf20Sopenharmony_ci		return -ECONNRESET;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	memset(infop, 0, sizeof(term_info));
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	skb_copy_bits(skb, srx->skb_offset, infop, to_copy);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	op = __rdmap_get_opcode(&term_info.ctrl);
9248c2ecf20Sopenharmony_ci	if (op >= RDMAP_TERMINATE)
9258c2ecf20Sopenharmony_ci		goto out;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	infop += to_copy;
9288c2ecf20Sopenharmony_ci	srx->skb_offset += to_copy;
9298c2ecf20Sopenharmony_ci	srx->skb_new -= to_copy;
9308c2ecf20Sopenharmony_ci	srx->skb_copied += to_copy;
9318c2ecf20Sopenharmony_ci	srx->fpdu_part_rcvd += to_copy;
9328c2ecf20Sopenharmony_ci	srx->fpdu_part_rem -= to_copy;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	to_copy = iwarp_pktinfo[op].hdr_len - to_copy;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	/* Again, no network fragmented TERM's */
9378c2ecf20Sopenharmony_ci	if (to_copy + MPA_CRC_SIZE > srx->skb_new)
9388c2ecf20Sopenharmony_ci		return -ECONNRESET;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	skb_copy_bits(skb, srx->skb_offset, infop, to_copy);
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	if (term->flag_r) {
9438c2ecf20Sopenharmony_ci		siw_dbg_qp(qp, "TERM reports RDMAP hdr type %u, len %u (%s)\n",
9448c2ecf20Sopenharmony_ci			   op, be16_to_cpu(term_info.ctrl.mpa_len),
9458c2ecf20Sopenharmony_ci			   term->flag_m ? "valid" : "invalid");
9468c2ecf20Sopenharmony_ci	} else if (term->flag_d) {
9478c2ecf20Sopenharmony_ci		siw_dbg_qp(qp, "TERM reports DDP hdr type %u, len %u (%s)\n",
9488c2ecf20Sopenharmony_ci			   op, be16_to_cpu(term_info.ctrl.mpa_len),
9498c2ecf20Sopenharmony_ci			   term->flag_m ? "valid" : "invalid");
9508c2ecf20Sopenharmony_ci	}
9518c2ecf20Sopenharmony_ciout:
9528c2ecf20Sopenharmony_ci	srx->skb_new -= to_copy;
9538c2ecf20Sopenharmony_ci	srx->skb_offset += to_copy;
9548c2ecf20Sopenharmony_ci	srx->skb_copied += to_copy;
9558c2ecf20Sopenharmony_ci	srx->fpdu_part_rcvd += to_copy;
9568c2ecf20Sopenharmony_ci	srx->fpdu_part_rem -= to_copy;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	return -ECONNRESET;
9598c2ecf20Sopenharmony_ci}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_cistatic int siw_get_trailer(struct siw_qp *qp, struct siw_rx_stream *srx)
9628c2ecf20Sopenharmony_ci{
9638c2ecf20Sopenharmony_ci	struct sk_buff *skb = srx->skb;
9648c2ecf20Sopenharmony_ci	int avail = min(srx->skb_new, srx->fpdu_part_rem);
9658c2ecf20Sopenharmony_ci	u8 *tbuf = (u8 *)&srx->trailer.crc - srx->pad;
9668c2ecf20Sopenharmony_ci	__wsum crc_in, crc_own = 0;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	siw_dbg_qp(qp, "expected %d, available %d, pad %u\n",
9698c2ecf20Sopenharmony_ci		   srx->fpdu_part_rem, srx->skb_new, srx->pad);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	skb_copy_bits(skb, srx->skb_offset, tbuf, avail);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	srx->skb_new -= avail;
9748c2ecf20Sopenharmony_ci	srx->skb_offset += avail;
9758c2ecf20Sopenharmony_ci	srx->skb_copied += avail;
9768c2ecf20Sopenharmony_ci	srx->fpdu_part_rem -= avail;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	if (srx->fpdu_part_rem)
9798c2ecf20Sopenharmony_ci		return -EAGAIN;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	if (!srx->mpa_crc_hd)
9828c2ecf20Sopenharmony_ci		return 0;
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	if (srx->pad)
9858c2ecf20Sopenharmony_ci		crypto_shash_update(srx->mpa_crc_hd, tbuf, srx->pad);
9868c2ecf20Sopenharmony_ci	/*
9878c2ecf20Sopenharmony_ci	 * CRC32 is computed, transmitted and received directly in NBO,
9888c2ecf20Sopenharmony_ci	 * so there's never a reason to convert byte order.
9898c2ecf20Sopenharmony_ci	 */
9908c2ecf20Sopenharmony_ci	crypto_shash_final(srx->mpa_crc_hd, (u8 *)&crc_own);
9918c2ecf20Sopenharmony_ci	crc_in = (__force __wsum)srx->trailer.crc;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	if (unlikely(crc_in != crc_own)) {
9948c2ecf20Sopenharmony_ci		pr_warn("siw: crc error. in: %08x, own %08x, op %u\n",
9958c2ecf20Sopenharmony_ci			crc_in, crc_own, qp->rx_stream.rdmap_op);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci		siw_init_terminate(qp, TERM_ERROR_LAYER_LLP,
9988c2ecf20Sopenharmony_ci				   LLP_ETYPE_MPA,
9998c2ecf20Sopenharmony_ci				   LLP_ECODE_RECEIVED_CRC, 0);
10008c2ecf20Sopenharmony_ci		return -EINVAL;
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_ci	return 0;
10038c2ecf20Sopenharmony_ci}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci#define MIN_DDP_HDR sizeof(struct iwarp_ctrl_tagged)
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_cistatic int siw_get_hdr(struct siw_rx_stream *srx)
10088c2ecf20Sopenharmony_ci{
10098c2ecf20Sopenharmony_ci	struct sk_buff *skb = srx->skb;
10108c2ecf20Sopenharmony_ci	struct siw_qp *qp = rx_qp(srx);
10118c2ecf20Sopenharmony_ci	struct iwarp_ctrl *c_hdr = &srx->hdr.ctrl;
10128c2ecf20Sopenharmony_ci	struct siw_rx_fpdu *frx;
10138c2ecf20Sopenharmony_ci	u8 opcode;
10148c2ecf20Sopenharmony_ci	int bytes;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	if (srx->fpdu_part_rcvd < MIN_DDP_HDR) {
10178c2ecf20Sopenharmony_ci		/*
10188c2ecf20Sopenharmony_ci		 * copy a mimimum sized (tagged) DDP frame control part
10198c2ecf20Sopenharmony_ci		 */
10208c2ecf20Sopenharmony_ci		bytes = min_t(int, srx->skb_new,
10218c2ecf20Sopenharmony_ci			      MIN_DDP_HDR - srx->fpdu_part_rcvd);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci		skb_copy_bits(skb, srx->skb_offset,
10248c2ecf20Sopenharmony_ci			      (char *)c_hdr + srx->fpdu_part_rcvd, bytes);
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci		srx->fpdu_part_rcvd += bytes;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci		srx->skb_new -= bytes;
10298c2ecf20Sopenharmony_ci		srx->skb_offset += bytes;
10308c2ecf20Sopenharmony_ci		srx->skb_copied += bytes;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci		if (srx->fpdu_part_rcvd < MIN_DDP_HDR)
10338c2ecf20Sopenharmony_ci			return -EAGAIN;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci		if (unlikely(__ddp_get_version(c_hdr) != DDP_VERSION)) {
10368c2ecf20Sopenharmony_ci			enum ddp_etype etype;
10378c2ecf20Sopenharmony_ci			enum ddp_ecode ecode;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci			pr_warn("siw: received ddp version unsupported %d\n",
10408c2ecf20Sopenharmony_ci				__ddp_get_version(c_hdr));
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci			if (c_hdr->ddp_rdmap_ctrl & DDP_FLAG_TAGGED) {
10438c2ecf20Sopenharmony_ci				etype = DDP_ETYPE_TAGGED_BUF;
10448c2ecf20Sopenharmony_ci				ecode = DDP_ECODE_T_VERSION;
10458c2ecf20Sopenharmony_ci			} else {
10468c2ecf20Sopenharmony_ci				etype = DDP_ETYPE_UNTAGGED_BUF;
10478c2ecf20Sopenharmony_ci				ecode = DDP_ECODE_UT_VERSION;
10488c2ecf20Sopenharmony_ci			}
10498c2ecf20Sopenharmony_ci			siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_DDP,
10508c2ecf20Sopenharmony_ci					   etype, ecode, 0);
10518c2ecf20Sopenharmony_ci			return -EINVAL;
10528c2ecf20Sopenharmony_ci		}
10538c2ecf20Sopenharmony_ci		if (unlikely(__rdmap_get_version(c_hdr) != RDMAP_VERSION)) {
10548c2ecf20Sopenharmony_ci			pr_warn("siw: received rdmap version unsupported %d\n",
10558c2ecf20Sopenharmony_ci				__rdmap_get_version(c_hdr));
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci			siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_RDMAP,
10588c2ecf20Sopenharmony_ci					   RDMAP_ETYPE_REMOTE_OPERATION,
10598c2ecf20Sopenharmony_ci					   RDMAP_ECODE_VERSION, 0);
10608c2ecf20Sopenharmony_ci			return -EINVAL;
10618c2ecf20Sopenharmony_ci		}
10628c2ecf20Sopenharmony_ci		opcode = __rdmap_get_opcode(c_hdr);
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci		if (opcode > RDMAP_TERMINATE) {
10658c2ecf20Sopenharmony_ci			pr_warn("siw: received unknown packet type %u\n",
10668c2ecf20Sopenharmony_ci				opcode);
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci			siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_RDMAP,
10698c2ecf20Sopenharmony_ci					   RDMAP_ETYPE_REMOTE_OPERATION,
10708c2ecf20Sopenharmony_ci					   RDMAP_ECODE_OPCODE, 0);
10718c2ecf20Sopenharmony_ci			return -EINVAL;
10728c2ecf20Sopenharmony_ci		}
10738c2ecf20Sopenharmony_ci		siw_dbg_qp(rx_qp(srx), "new header, opcode %u\n", opcode);
10748c2ecf20Sopenharmony_ci	} else {
10758c2ecf20Sopenharmony_ci		opcode = __rdmap_get_opcode(c_hdr);
10768c2ecf20Sopenharmony_ci	}
10778c2ecf20Sopenharmony_ci	set_rx_fpdu_context(qp, opcode);
10788c2ecf20Sopenharmony_ci	frx = qp->rx_fpdu;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	/*
10818c2ecf20Sopenharmony_ci	 * Figure out len of current hdr: variable length of
10828c2ecf20Sopenharmony_ci	 * iwarp hdr may force us to copy hdr information in
10838c2ecf20Sopenharmony_ci	 * two steps. Only tagged DDP messages are already
10848c2ecf20Sopenharmony_ci	 * completely received.
10858c2ecf20Sopenharmony_ci	 */
10868c2ecf20Sopenharmony_ci	if (iwarp_pktinfo[opcode].hdr_len > sizeof(struct iwarp_ctrl_tagged)) {
10878c2ecf20Sopenharmony_ci		int hdrlen = iwarp_pktinfo[opcode].hdr_len;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci		bytes = min_t(int, hdrlen - MIN_DDP_HDR, srx->skb_new);
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci		skb_copy_bits(skb, srx->skb_offset,
10928c2ecf20Sopenharmony_ci			      (char *)c_hdr + srx->fpdu_part_rcvd, bytes);
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci		srx->fpdu_part_rcvd += bytes;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci		srx->skb_new -= bytes;
10978c2ecf20Sopenharmony_ci		srx->skb_offset += bytes;
10988c2ecf20Sopenharmony_ci		srx->skb_copied += bytes;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci		if (srx->fpdu_part_rcvd < hdrlen)
11018c2ecf20Sopenharmony_ci			return -EAGAIN;
11028c2ecf20Sopenharmony_ci	}
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	/*
11058c2ecf20Sopenharmony_ci	 * DDP/RDMAP header receive completed. Check if the current
11068c2ecf20Sopenharmony_ci	 * DDP segment starts a new RDMAP message or continues a previously
11078c2ecf20Sopenharmony_ci	 * started RDMAP message.
11088c2ecf20Sopenharmony_ci	 *
11098c2ecf20Sopenharmony_ci	 * Alternating reception of DDP segments (or FPDUs) from incomplete
11108c2ecf20Sopenharmony_ci	 * tagged and untagged RDMAP messages is supported, as long as
11118c2ecf20Sopenharmony_ci	 * the current tagged or untagged message gets eventually completed
11128c2ecf20Sopenharmony_ci	 * w/o intersection from another message of the same type
11138c2ecf20Sopenharmony_ci	 * (tagged/untagged). E.g., a WRITE can get intersected by a SEND,
11148c2ecf20Sopenharmony_ci	 * but not by a READ RESPONSE etc.
11158c2ecf20Sopenharmony_ci	 */
11168c2ecf20Sopenharmony_ci	if (srx->mpa_crc_hd) {
11178c2ecf20Sopenharmony_ci		/*
11188c2ecf20Sopenharmony_ci		 * Restart CRC computation
11198c2ecf20Sopenharmony_ci		 */
11208c2ecf20Sopenharmony_ci		crypto_shash_init(srx->mpa_crc_hd);
11218c2ecf20Sopenharmony_ci		crypto_shash_update(srx->mpa_crc_hd, (u8 *)c_hdr,
11228c2ecf20Sopenharmony_ci				    srx->fpdu_part_rcvd);
11238c2ecf20Sopenharmony_ci	}
11248c2ecf20Sopenharmony_ci	if (frx->more_ddp_segs) {
11258c2ecf20Sopenharmony_ci		frx->first_ddp_seg = 0;
11268c2ecf20Sopenharmony_ci		if (frx->prev_rdmap_op != opcode) {
11278c2ecf20Sopenharmony_ci			pr_warn("siw: packet intersection: %u : %u\n",
11288c2ecf20Sopenharmony_ci				frx->prev_rdmap_op, opcode);
11298c2ecf20Sopenharmony_ci			/*
11308c2ecf20Sopenharmony_ci			 * The last inbound RDMA operation of same type
11318c2ecf20Sopenharmony_ci			 * (tagged or untagged) is left unfinished.
11328c2ecf20Sopenharmony_ci			 * To complete it in error, make it the current
11338c2ecf20Sopenharmony_ci			 * operation again, even with the header already
11348c2ecf20Sopenharmony_ci			 * overwritten. For error handling, only the opcode
11358c2ecf20Sopenharmony_ci			 * and current rx context are relevant.
11368c2ecf20Sopenharmony_ci			 */
11378c2ecf20Sopenharmony_ci			set_rx_fpdu_context(qp, frx->prev_rdmap_op);
11388c2ecf20Sopenharmony_ci			__rdmap_set_opcode(c_hdr, frx->prev_rdmap_op);
11398c2ecf20Sopenharmony_ci			return -EPROTO;
11408c2ecf20Sopenharmony_ci		}
11418c2ecf20Sopenharmony_ci	} else {
11428c2ecf20Sopenharmony_ci		frx->prev_rdmap_op = opcode;
11438c2ecf20Sopenharmony_ci		frx->first_ddp_seg = 1;
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci	frx->more_ddp_segs = c_hdr->ddp_rdmap_ctrl & DDP_FLAG_LAST ? 0 : 1;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	return 0;
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_cistatic int siw_check_tx_fence(struct siw_qp *qp)
11518c2ecf20Sopenharmony_ci{
11528c2ecf20Sopenharmony_ci	struct siw_wqe *tx_waiting = tx_wqe(qp);
11538c2ecf20Sopenharmony_ci	struct siw_sqe *rreq;
11548c2ecf20Sopenharmony_ci	int resume_tx = 0, rv = 0;
11558c2ecf20Sopenharmony_ci	unsigned long flags;
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	spin_lock_irqsave(&qp->orq_lock, flags);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	/* free current orq entry */
11608c2ecf20Sopenharmony_ci	rreq = orq_get_current(qp);
11618c2ecf20Sopenharmony_ci	WRITE_ONCE(rreq->flags, 0);
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	qp->orq_get++;
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	if (qp->tx_ctx.orq_fence) {
11668c2ecf20Sopenharmony_ci		if (unlikely(tx_waiting->wr_status != SIW_WR_QUEUED)) {
11678c2ecf20Sopenharmony_ci			pr_warn("siw: [QP %u]: fence resume: bad status %d\n",
11688c2ecf20Sopenharmony_ci				qp_id(qp), tx_waiting->wr_status);
11698c2ecf20Sopenharmony_ci			rv = -EPROTO;
11708c2ecf20Sopenharmony_ci			goto out;
11718c2ecf20Sopenharmony_ci		}
11728c2ecf20Sopenharmony_ci		/* resume SQ processing, if possible */
11738c2ecf20Sopenharmony_ci		if (tx_waiting->sqe.opcode == SIW_OP_READ ||
11748c2ecf20Sopenharmony_ci		    tx_waiting->sqe.opcode == SIW_OP_READ_LOCAL_INV) {
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci			/* SQ processing was stopped because of a full ORQ */
11778c2ecf20Sopenharmony_ci			rreq = orq_get_free(qp);
11788c2ecf20Sopenharmony_ci			if (unlikely(!rreq)) {
11798c2ecf20Sopenharmony_ci				pr_warn("siw: [QP %u]: no ORQE\n", qp_id(qp));
11808c2ecf20Sopenharmony_ci				rv = -EPROTO;
11818c2ecf20Sopenharmony_ci				goto out;
11828c2ecf20Sopenharmony_ci			}
11838c2ecf20Sopenharmony_ci			siw_read_to_orq(rreq, &tx_waiting->sqe);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci			qp->orq_put++;
11868c2ecf20Sopenharmony_ci			qp->tx_ctx.orq_fence = 0;
11878c2ecf20Sopenharmony_ci			resume_tx = 1;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci		} else if (siw_orq_empty(qp)) {
11908c2ecf20Sopenharmony_ci			/*
11918c2ecf20Sopenharmony_ci			 * SQ processing was stopped by fenced work request.
11928c2ecf20Sopenharmony_ci			 * Resume since all previous Read's are now completed.
11938c2ecf20Sopenharmony_ci			 */
11948c2ecf20Sopenharmony_ci			qp->tx_ctx.orq_fence = 0;
11958c2ecf20Sopenharmony_ci			resume_tx = 1;
11968c2ecf20Sopenharmony_ci		}
11978c2ecf20Sopenharmony_ci	}
11988c2ecf20Sopenharmony_ciout:
11998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&qp->orq_lock, flags);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	if (resume_tx)
12028c2ecf20Sopenharmony_ci		rv = siw_sq_start(qp);
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	return rv;
12058c2ecf20Sopenharmony_ci}
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci/*
12088c2ecf20Sopenharmony_ci * siw_rdmap_complete()
12098c2ecf20Sopenharmony_ci *
12108c2ecf20Sopenharmony_ci * Complete processing of an RDMA message after receiving all
12118c2ecf20Sopenharmony_ci * DDP segmens or ABort processing after encountering error case.
12128c2ecf20Sopenharmony_ci *
12138c2ecf20Sopenharmony_ci *   o SENDs + RRESPs will need for completion,
12148c2ecf20Sopenharmony_ci *   o RREQs need for  READ RESPONSE initialization
12158c2ecf20Sopenharmony_ci *   o WRITEs need memory dereferencing
12168c2ecf20Sopenharmony_ci *
12178c2ecf20Sopenharmony_ci * TODO: Failed WRITEs need local error to be surfaced.
12188c2ecf20Sopenharmony_ci */
12198c2ecf20Sopenharmony_cistatic int siw_rdmap_complete(struct siw_qp *qp, int error)
12208c2ecf20Sopenharmony_ci{
12218c2ecf20Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
12228c2ecf20Sopenharmony_ci	struct siw_wqe *wqe = rx_wqe(qp->rx_fpdu);
12238c2ecf20Sopenharmony_ci	enum siw_wc_status wc_status = wqe->wc_status;
12248c2ecf20Sopenharmony_ci	u8 opcode = __rdmap_get_opcode(&srx->hdr.ctrl);
12258c2ecf20Sopenharmony_ci	int rv = 0;
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	switch (opcode) {
12288c2ecf20Sopenharmony_ci	case RDMAP_SEND_SE:
12298c2ecf20Sopenharmony_ci	case RDMAP_SEND_SE_INVAL:
12308c2ecf20Sopenharmony_ci		wqe->rqe.flags |= SIW_WQE_SOLICITED;
12318c2ecf20Sopenharmony_ci		fallthrough;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	case RDMAP_SEND:
12348c2ecf20Sopenharmony_ci	case RDMAP_SEND_INVAL:
12358c2ecf20Sopenharmony_ci		if (wqe->wr_status == SIW_WR_IDLE)
12368c2ecf20Sopenharmony_ci			break;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci		srx->ddp_msn[RDMAP_UNTAGGED_QN_SEND]++;
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci		if (error != 0 && wc_status == SIW_WC_SUCCESS)
12418c2ecf20Sopenharmony_ci			wc_status = SIW_WC_GENERAL_ERR;
12428c2ecf20Sopenharmony_ci		/*
12438c2ecf20Sopenharmony_ci		 * Handle STag invalidation request
12448c2ecf20Sopenharmony_ci		 */
12458c2ecf20Sopenharmony_ci		if (wc_status == SIW_WC_SUCCESS &&
12468c2ecf20Sopenharmony_ci		    (opcode == RDMAP_SEND_INVAL ||
12478c2ecf20Sopenharmony_ci		     opcode == RDMAP_SEND_SE_INVAL)) {
12488c2ecf20Sopenharmony_ci			rv = siw_invalidate_stag(qp->pd, srx->inval_stag);
12498c2ecf20Sopenharmony_ci			if (rv) {
12508c2ecf20Sopenharmony_ci				siw_init_terminate(
12518c2ecf20Sopenharmony_ci					qp, TERM_ERROR_LAYER_RDMAP,
12528c2ecf20Sopenharmony_ci					rv == -EACCES ?
12538c2ecf20Sopenharmony_ci						RDMAP_ETYPE_REMOTE_PROTECTION :
12548c2ecf20Sopenharmony_ci						RDMAP_ETYPE_REMOTE_OPERATION,
12558c2ecf20Sopenharmony_ci					RDMAP_ECODE_CANNOT_INVALIDATE, 0);
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci				wc_status = SIW_WC_REM_INV_REQ_ERR;
12588c2ecf20Sopenharmony_ci			}
12598c2ecf20Sopenharmony_ci			rv = siw_rqe_complete(qp, &wqe->rqe, wqe->processed,
12608c2ecf20Sopenharmony_ci					      rv ? 0 : srx->inval_stag,
12618c2ecf20Sopenharmony_ci					      wc_status);
12628c2ecf20Sopenharmony_ci		} else {
12638c2ecf20Sopenharmony_ci			rv = siw_rqe_complete(qp, &wqe->rqe, wqe->processed,
12648c2ecf20Sopenharmony_ci					      0, wc_status);
12658c2ecf20Sopenharmony_ci		}
12668c2ecf20Sopenharmony_ci		siw_wqe_put_mem(wqe, SIW_OP_RECEIVE);
12678c2ecf20Sopenharmony_ci		break;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	case RDMAP_RDMA_READ_RESP:
12708c2ecf20Sopenharmony_ci		if (wqe->wr_status == SIW_WR_IDLE)
12718c2ecf20Sopenharmony_ci			break;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci		if (error != 0) {
12748c2ecf20Sopenharmony_ci			if ((srx->state == SIW_GET_HDR &&
12758c2ecf20Sopenharmony_ci			     qp->rx_fpdu->first_ddp_seg) || error == -ENODATA)
12768c2ecf20Sopenharmony_ci				/* possible RREQ in ORQ left untouched */
12778c2ecf20Sopenharmony_ci				break;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci			if (wc_status == SIW_WC_SUCCESS)
12808c2ecf20Sopenharmony_ci				wc_status = SIW_WC_GENERAL_ERR;
12818c2ecf20Sopenharmony_ci		} else if (rdma_is_kernel_res(&qp->base_qp.res) &&
12828c2ecf20Sopenharmony_ci			   rx_type(wqe) == SIW_OP_READ_LOCAL_INV) {
12838c2ecf20Sopenharmony_ci			/*
12848c2ecf20Sopenharmony_ci			 * Handle any STag invalidation request
12858c2ecf20Sopenharmony_ci			 */
12868c2ecf20Sopenharmony_ci			rv = siw_invalidate_stag(qp->pd, wqe->sqe.sge[0].lkey);
12878c2ecf20Sopenharmony_ci			if (rv) {
12888c2ecf20Sopenharmony_ci				siw_init_terminate(qp, TERM_ERROR_LAYER_RDMAP,
12898c2ecf20Sopenharmony_ci						   RDMAP_ETYPE_CATASTROPHIC,
12908c2ecf20Sopenharmony_ci						   RDMAP_ECODE_UNSPECIFIED, 0);
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci				if (wc_status == SIW_WC_SUCCESS) {
12938c2ecf20Sopenharmony_ci					wc_status = SIW_WC_GENERAL_ERR;
12948c2ecf20Sopenharmony_ci					error = rv;
12958c2ecf20Sopenharmony_ci				}
12968c2ecf20Sopenharmony_ci			}
12978c2ecf20Sopenharmony_ci		}
12988c2ecf20Sopenharmony_ci		/*
12998c2ecf20Sopenharmony_ci		 * All errors turn the wqe into signalled.
13008c2ecf20Sopenharmony_ci		 */
13018c2ecf20Sopenharmony_ci		if ((wqe->sqe.flags & SIW_WQE_SIGNALLED) || error != 0)
13028c2ecf20Sopenharmony_ci			rv = siw_sqe_complete(qp, &wqe->sqe, wqe->processed,
13038c2ecf20Sopenharmony_ci					      wc_status);
13048c2ecf20Sopenharmony_ci		siw_wqe_put_mem(wqe, SIW_OP_READ);
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci		if (!error) {
13078c2ecf20Sopenharmony_ci			rv = siw_check_tx_fence(qp);
13088c2ecf20Sopenharmony_ci		} else {
13098c2ecf20Sopenharmony_ci			/* Disable current ORQ element */
13108c2ecf20Sopenharmony_ci			if (qp->attrs.orq_size)
13118c2ecf20Sopenharmony_ci				WRITE_ONCE(orq_get_current(qp)->flags, 0);
13128c2ecf20Sopenharmony_ci		}
13138c2ecf20Sopenharmony_ci		break;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	case RDMAP_RDMA_READ_REQ:
13168c2ecf20Sopenharmony_ci		if (!error) {
13178c2ecf20Sopenharmony_ci			rv = siw_init_rresp(qp, srx);
13188c2ecf20Sopenharmony_ci			srx->ddp_msn[RDMAP_UNTAGGED_QN_RDMA_READ]++;
13198c2ecf20Sopenharmony_ci		}
13208c2ecf20Sopenharmony_ci		break;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	case RDMAP_RDMA_WRITE:
13238c2ecf20Sopenharmony_ci		if (wqe->wr_status == SIW_WR_IDLE)
13248c2ecf20Sopenharmony_ci			break;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci		/*
13278c2ecf20Sopenharmony_ci		 * Free References from memory object if
13288c2ecf20Sopenharmony_ci		 * attached to receive context (inbound WRITE).
13298c2ecf20Sopenharmony_ci		 * While a zero-length WRITE is allowed,
13308c2ecf20Sopenharmony_ci		 * no memory reference got created.
13318c2ecf20Sopenharmony_ci		 */
13328c2ecf20Sopenharmony_ci		if (rx_mem(&qp->rx_tagged)) {
13338c2ecf20Sopenharmony_ci			siw_mem_put(rx_mem(&qp->rx_tagged));
13348c2ecf20Sopenharmony_ci			rx_mem(&qp->rx_tagged) = NULL;
13358c2ecf20Sopenharmony_ci		}
13368c2ecf20Sopenharmony_ci		break;
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	default:
13398c2ecf20Sopenharmony_ci		break;
13408c2ecf20Sopenharmony_ci	}
13418c2ecf20Sopenharmony_ci	wqe->wr_status = SIW_WR_IDLE;
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	return rv;
13448c2ecf20Sopenharmony_ci}
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci/*
13478c2ecf20Sopenharmony_ci * siw_tcp_rx_data()
13488c2ecf20Sopenharmony_ci *
13498c2ecf20Sopenharmony_ci * Main routine to consume inbound TCP payload
13508c2ecf20Sopenharmony_ci *
13518c2ecf20Sopenharmony_ci * @rd_desc:	read descriptor
13528c2ecf20Sopenharmony_ci * @skb:	socket buffer
13538c2ecf20Sopenharmony_ci * @off:	offset in skb
13548c2ecf20Sopenharmony_ci * @len:	skb->len - offset : payload in skb
13558c2ecf20Sopenharmony_ci */
13568c2ecf20Sopenharmony_ciint siw_tcp_rx_data(read_descriptor_t *rd_desc, struct sk_buff *skb,
13578c2ecf20Sopenharmony_ci		    unsigned int off, size_t len)
13588c2ecf20Sopenharmony_ci{
13598c2ecf20Sopenharmony_ci	struct siw_qp *qp = rd_desc->arg.data;
13608c2ecf20Sopenharmony_ci	struct siw_rx_stream *srx = &qp->rx_stream;
13618c2ecf20Sopenharmony_ci	int rv;
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	srx->skb = skb;
13648c2ecf20Sopenharmony_ci	srx->skb_new = skb->len - off;
13658c2ecf20Sopenharmony_ci	srx->skb_offset = off;
13668c2ecf20Sopenharmony_ci	srx->skb_copied = 0;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	siw_dbg_qp(qp, "new data, len %d\n", srx->skb_new);
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	while (srx->skb_new) {
13718c2ecf20Sopenharmony_ci		int run_completion = 1;
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci		if (unlikely(srx->rx_suspend)) {
13748c2ecf20Sopenharmony_ci			/* Do not process any more data */
13758c2ecf20Sopenharmony_ci			srx->skb_copied += srx->skb_new;
13768c2ecf20Sopenharmony_ci			break;
13778c2ecf20Sopenharmony_ci		}
13788c2ecf20Sopenharmony_ci		switch (srx->state) {
13798c2ecf20Sopenharmony_ci		case SIW_GET_HDR:
13808c2ecf20Sopenharmony_ci			rv = siw_get_hdr(srx);
13818c2ecf20Sopenharmony_ci			if (!rv) {
13828c2ecf20Sopenharmony_ci				srx->fpdu_part_rem =
13838c2ecf20Sopenharmony_ci					be16_to_cpu(srx->hdr.ctrl.mpa_len) -
13848c2ecf20Sopenharmony_ci					srx->fpdu_part_rcvd + MPA_HDR_SIZE;
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci				if (srx->fpdu_part_rem)
13878c2ecf20Sopenharmony_ci					srx->pad = -srx->fpdu_part_rem & 0x3;
13888c2ecf20Sopenharmony_ci				else
13898c2ecf20Sopenharmony_ci					srx->pad = 0;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci				srx->state = SIW_GET_DATA_START;
13928c2ecf20Sopenharmony_ci				srx->fpdu_part_rcvd = 0;
13938c2ecf20Sopenharmony_ci			}
13948c2ecf20Sopenharmony_ci			break;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci		case SIW_GET_DATA_MORE:
13978c2ecf20Sopenharmony_ci			/*
13988c2ecf20Sopenharmony_ci			 * Another data fragment of the same DDP segment.
13998c2ecf20Sopenharmony_ci			 * Setting first_ddp_seg = 0 avoids repeating
14008c2ecf20Sopenharmony_ci			 * initializations that shall occur only once per
14018c2ecf20Sopenharmony_ci			 * DDP segment.
14028c2ecf20Sopenharmony_ci			 */
14038c2ecf20Sopenharmony_ci			qp->rx_fpdu->first_ddp_seg = 0;
14048c2ecf20Sopenharmony_ci			fallthrough;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci		case SIW_GET_DATA_START:
14078c2ecf20Sopenharmony_ci			/*
14088c2ecf20Sopenharmony_ci			 * Headers will be checked by the opcode-specific
14098c2ecf20Sopenharmony_ci			 * data receive function below.
14108c2ecf20Sopenharmony_ci			 */
14118c2ecf20Sopenharmony_ci			rv = iwarp_pktinfo[qp->rx_stream.rdmap_op].rx_data(qp);
14128c2ecf20Sopenharmony_ci			if (!rv) {
14138c2ecf20Sopenharmony_ci				int mpa_len =
14148c2ecf20Sopenharmony_ci					be16_to_cpu(srx->hdr.ctrl.mpa_len)
14158c2ecf20Sopenharmony_ci					+ MPA_HDR_SIZE;
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci				srx->fpdu_part_rem = (-mpa_len & 0x3)
14188c2ecf20Sopenharmony_ci						      + MPA_CRC_SIZE;
14198c2ecf20Sopenharmony_ci				srx->fpdu_part_rcvd = 0;
14208c2ecf20Sopenharmony_ci				srx->state = SIW_GET_TRAILER;
14218c2ecf20Sopenharmony_ci			} else {
14228c2ecf20Sopenharmony_ci				if (unlikely(rv == -ECONNRESET))
14238c2ecf20Sopenharmony_ci					run_completion = 0;
14248c2ecf20Sopenharmony_ci				else
14258c2ecf20Sopenharmony_ci					srx->state = SIW_GET_DATA_MORE;
14268c2ecf20Sopenharmony_ci			}
14278c2ecf20Sopenharmony_ci			break;
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci		case SIW_GET_TRAILER:
14308c2ecf20Sopenharmony_ci			/*
14318c2ecf20Sopenharmony_ci			 * read CRC + any padding
14328c2ecf20Sopenharmony_ci			 */
14338c2ecf20Sopenharmony_ci			rv = siw_get_trailer(qp, srx);
14348c2ecf20Sopenharmony_ci			if (likely(!rv)) {
14358c2ecf20Sopenharmony_ci				/*
14368c2ecf20Sopenharmony_ci				 * FPDU completed.
14378c2ecf20Sopenharmony_ci				 * complete RDMAP message if last fragment
14388c2ecf20Sopenharmony_ci				 */
14398c2ecf20Sopenharmony_ci				srx->state = SIW_GET_HDR;
14408c2ecf20Sopenharmony_ci				srx->fpdu_part_rcvd = 0;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci				if (!(srx->hdr.ctrl.ddp_rdmap_ctrl &
14438c2ecf20Sopenharmony_ci				      DDP_FLAG_LAST))
14448c2ecf20Sopenharmony_ci					/* more frags */
14458c2ecf20Sopenharmony_ci					break;
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci				rv = siw_rdmap_complete(qp, 0);
14488c2ecf20Sopenharmony_ci				run_completion = 0;
14498c2ecf20Sopenharmony_ci			}
14508c2ecf20Sopenharmony_ci			break;
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci		default:
14538c2ecf20Sopenharmony_ci			pr_warn("QP[%u]: RX out of state\n", qp_id(qp));
14548c2ecf20Sopenharmony_ci			rv = -EPROTO;
14558c2ecf20Sopenharmony_ci			run_completion = 0;
14568c2ecf20Sopenharmony_ci		}
14578c2ecf20Sopenharmony_ci		if (unlikely(rv != 0 && rv != -EAGAIN)) {
14588c2ecf20Sopenharmony_ci			if ((srx->state > SIW_GET_HDR ||
14598c2ecf20Sopenharmony_ci			     qp->rx_fpdu->more_ddp_segs) && run_completion)
14608c2ecf20Sopenharmony_ci				siw_rdmap_complete(qp, rv);
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci			siw_dbg_qp(qp, "rx error %d, rx state %d\n", rv,
14638c2ecf20Sopenharmony_ci				   srx->state);
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci			siw_qp_cm_drop(qp, 1);
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci			break;
14688c2ecf20Sopenharmony_ci		}
14698c2ecf20Sopenharmony_ci		if (rv) {
14708c2ecf20Sopenharmony_ci			siw_dbg_qp(qp, "fpdu fragment, state %d, missing %d\n",
14718c2ecf20Sopenharmony_ci				   srx->state, srx->fpdu_part_rem);
14728c2ecf20Sopenharmony_ci			break;
14738c2ecf20Sopenharmony_ci		}
14748c2ecf20Sopenharmony_ci	}
14758c2ecf20Sopenharmony_ci	return srx->skb_copied;
14768c2ecf20Sopenharmony_ci}
1477