162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
462306a36Sopenharmony_ci * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/skbuff.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "rxe.h"
1062306a36Sopenharmony_ci#include "rxe_loc.h"
1162306a36Sopenharmony_ci#include "rxe_queue.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic char *resp_state_name[] = {
1462306a36Sopenharmony_ci	[RESPST_NONE]				= "NONE",
1562306a36Sopenharmony_ci	[RESPST_GET_REQ]			= "GET_REQ",
1662306a36Sopenharmony_ci	[RESPST_CHK_PSN]			= "CHK_PSN",
1762306a36Sopenharmony_ci	[RESPST_CHK_OP_SEQ]			= "CHK_OP_SEQ",
1862306a36Sopenharmony_ci	[RESPST_CHK_OP_VALID]			= "CHK_OP_VALID",
1962306a36Sopenharmony_ci	[RESPST_CHK_RESOURCE]			= "CHK_RESOURCE",
2062306a36Sopenharmony_ci	[RESPST_CHK_LENGTH]			= "CHK_LENGTH",
2162306a36Sopenharmony_ci	[RESPST_CHK_RKEY]			= "CHK_RKEY",
2262306a36Sopenharmony_ci	[RESPST_EXECUTE]			= "EXECUTE",
2362306a36Sopenharmony_ci	[RESPST_READ_REPLY]			= "READ_REPLY",
2462306a36Sopenharmony_ci	[RESPST_ATOMIC_REPLY]			= "ATOMIC_REPLY",
2562306a36Sopenharmony_ci	[RESPST_ATOMIC_WRITE_REPLY]		= "ATOMIC_WRITE_REPLY",
2662306a36Sopenharmony_ci	[RESPST_PROCESS_FLUSH]			= "PROCESS_FLUSH",
2762306a36Sopenharmony_ci	[RESPST_COMPLETE]			= "COMPLETE",
2862306a36Sopenharmony_ci	[RESPST_ACKNOWLEDGE]			= "ACKNOWLEDGE",
2962306a36Sopenharmony_ci	[RESPST_CLEANUP]			= "CLEANUP",
3062306a36Sopenharmony_ci	[RESPST_DUPLICATE_REQUEST]		= "DUPLICATE_REQUEST",
3162306a36Sopenharmony_ci	[RESPST_ERR_MALFORMED_WQE]		= "ERR_MALFORMED_WQE",
3262306a36Sopenharmony_ci	[RESPST_ERR_UNSUPPORTED_OPCODE]		= "ERR_UNSUPPORTED_OPCODE",
3362306a36Sopenharmony_ci	[RESPST_ERR_MISALIGNED_ATOMIC]		= "ERR_MISALIGNED_ATOMIC",
3462306a36Sopenharmony_ci	[RESPST_ERR_PSN_OUT_OF_SEQ]		= "ERR_PSN_OUT_OF_SEQ",
3562306a36Sopenharmony_ci	[RESPST_ERR_MISSING_OPCODE_FIRST]	= "ERR_MISSING_OPCODE_FIRST",
3662306a36Sopenharmony_ci	[RESPST_ERR_MISSING_OPCODE_LAST_C]	= "ERR_MISSING_OPCODE_LAST_C",
3762306a36Sopenharmony_ci	[RESPST_ERR_MISSING_OPCODE_LAST_D1E]	= "ERR_MISSING_OPCODE_LAST_D1E",
3862306a36Sopenharmony_ci	[RESPST_ERR_TOO_MANY_RDMA_ATM_REQ]	= "ERR_TOO_MANY_RDMA_ATM_REQ",
3962306a36Sopenharmony_ci	[RESPST_ERR_RNR]			= "ERR_RNR",
4062306a36Sopenharmony_ci	[RESPST_ERR_RKEY_VIOLATION]		= "ERR_RKEY_VIOLATION",
4162306a36Sopenharmony_ci	[RESPST_ERR_INVALIDATE_RKEY]		= "ERR_INVALIDATE_RKEY_VIOLATION",
4262306a36Sopenharmony_ci	[RESPST_ERR_LENGTH]			= "ERR_LENGTH",
4362306a36Sopenharmony_ci	[RESPST_ERR_CQ_OVERFLOW]		= "ERR_CQ_OVERFLOW",
4462306a36Sopenharmony_ci	[RESPST_ERROR]				= "ERROR",
4562306a36Sopenharmony_ci	[RESPST_DONE]				= "DONE",
4662306a36Sopenharmony_ci	[RESPST_EXIT]				= "EXIT",
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* rxe_recv calls here to add a request packet to the input queue */
5062306a36Sopenharmony_civoid rxe_resp_queue_pkt(struct rxe_qp *qp, struct sk_buff *skb)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	int must_sched;
5362306a36Sopenharmony_ci	struct rxe_pkt_info *pkt = SKB_TO_PKT(skb);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	skb_queue_tail(&qp->req_pkts, skb);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	must_sched = (pkt->opcode == IB_OPCODE_RC_RDMA_READ_REQUEST) ||
5862306a36Sopenharmony_ci			(skb_queue_len(&qp->req_pkts) > 1);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if (must_sched)
6162306a36Sopenharmony_ci		rxe_sched_task(&qp->resp.task);
6262306a36Sopenharmony_ci	else
6362306a36Sopenharmony_ci		rxe_run_task(&qp->resp.task);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic inline enum resp_states get_req(struct rxe_qp *qp,
6762306a36Sopenharmony_ci				       struct rxe_pkt_info **pkt_p)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct sk_buff *skb;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	skb = skb_peek(&qp->req_pkts);
7262306a36Sopenharmony_ci	if (!skb)
7362306a36Sopenharmony_ci		return RESPST_EXIT;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	*pkt_p = SKB_TO_PKT(skb);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return (qp->resp.res) ? RESPST_READ_REPLY : RESPST_CHK_PSN;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic enum resp_states check_psn(struct rxe_qp *qp,
8162306a36Sopenharmony_ci				  struct rxe_pkt_info *pkt)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	int diff = psn_compare(pkt->psn, qp->resp.psn);
8462306a36Sopenharmony_ci	struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	switch (qp_type(qp)) {
8762306a36Sopenharmony_ci	case IB_QPT_RC:
8862306a36Sopenharmony_ci		if (diff > 0) {
8962306a36Sopenharmony_ci			if (qp->resp.sent_psn_nak)
9062306a36Sopenharmony_ci				return RESPST_CLEANUP;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci			qp->resp.sent_psn_nak = 1;
9362306a36Sopenharmony_ci			rxe_counter_inc(rxe, RXE_CNT_OUT_OF_SEQ_REQ);
9462306a36Sopenharmony_ci			return RESPST_ERR_PSN_OUT_OF_SEQ;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		} else if (diff < 0) {
9762306a36Sopenharmony_ci			rxe_counter_inc(rxe, RXE_CNT_DUP_REQ);
9862306a36Sopenharmony_ci			return RESPST_DUPLICATE_REQUEST;
9962306a36Sopenharmony_ci		}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		if (qp->resp.sent_psn_nak)
10262306a36Sopenharmony_ci			qp->resp.sent_psn_nak = 0;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		break;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	case IB_QPT_UC:
10762306a36Sopenharmony_ci		if (qp->resp.drop_msg || diff != 0) {
10862306a36Sopenharmony_ci			if (pkt->mask & RXE_START_MASK) {
10962306a36Sopenharmony_ci				qp->resp.drop_msg = 0;
11062306a36Sopenharmony_ci				return RESPST_CHK_OP_SEQ;
11162306a36Sopenharmony_ci			}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci			qp->resp.drop_msg = 1;
11462306a36Sopenharmony_ci			return RESPST_CLEANUP;
11562306a36Sopenharmony_ci		}
11662306a36Sopenharmony_ci		break;
11762306a36Sopenharmony_ci	default:
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return RESPST_CHK_OP_SEQ;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic enum resp_states check_op_seq(struct rxe_qp *qp,
12562306a36Sopenharmony_ci				     struct rxe_pkt_info *pkt)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	switch (qp_type(qp)) {
12862306a36Sopenharmony_ci	case IB_QPT_RC:
12962306a36Sopenharmony_ci		switch (qp->resp.opcode) {
13062306a36Sopenharmony_ci		case IB_OPCODE_RC_SEND_FIRST:
13162306a36Sopenharmony_ci		case IB_OPCODE_RC_SEND_MIDDLE:
13262306a36Sopenharmony_ci			switch (pkt->opcode) {
13362306a36Sopenharmony_ci			case IB_OPCODE_RC_SEND_MIDDLE:
13462306a36Sopenharmony_ci			case IB_OPCODE_RC_SEND_LAST:
13562306a36Sopenharmony_ci			case IB_OPCODE_RC_SEND_LAST_WITH_IMMEDIATE:
13662306a36Sopenharmony_ci			case IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE:
13762306a36Sopenharmony_ci				return RESPST_CHK_OP_VALID;
13862306a36Sopenharmony_ci			default:
13962306a36Sopenharmony_ci				return RESPST_ERR_MISSING_OPCODE_LAST_C;
14062306a36Sopenharmony_ci			}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		case IB_OPCODE_RC_RDMA_WRITE_FIRST:
14362306a36Sopenharmony_ci		case IB_OPCODE_RC_RDMA_WRITE_MIDDLE:
14462306a36Sopenharmony_ci			switch (pkt->opcode) {
14562306a36Sopenharmony_ci			case IB_OPCODE_RC_RDMA_WRITE_MIDDLE:
14662306a36Sopenharmony_ci			case IB_OPCODE_RC_RDMA_WRITE_LAST:
14762306a36Sopenharmony_ci			case IB_OPCODE_RC_RDMA_WRITE_LAST_WITH_IMMEDIATE:
14862306a36Sopenharmony_ci				return RESPST_CHK_OP_VALID;
14962306a36Sopenharmony_ci			default:
15062306a36Sopenharmony_ci				return RESPST_ERR_MISSING_OPCODE_LAST_C;
15162306a36Sopenharmony_ci			}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		default:
15462306a36Sopenharmony_ci			switch (pkt->opcode) {
15562306a36Sopenharmony_ci			case IB_OPCODE_RC_SEND_MIDDLE:
15662306a36Sopenharmony_ci			case IB_OPCODE_RC_SEND_LAST:
15762306a36Sopenharmony_ci			case IB_OPCODE_RC_SEND_LAST_WITH_IMMEDIATE:
15862306a36Sopenharmony_ci			case IB_OPCODE_RC_SEND_LAST_WITH_INVALIDATE:
15962306a36Sopenharmony_ci			case IB_OPCODE_RC_RDMA_WRITE_MIDDLE:
16062306a36Sopenharmony_ci			case IB_OPCODE_RC_RDMA_WRITE_LAST:
16162306a36Sopenharmony_ci			case IB_OPCODE_RC_RDMA_WRITE_LAST_WITH_IMMEDIATE:
16262306a36Sopenharmony_ci				return RESPST_ERR_MISSING_OPCODE_FIRST;
16362306a36Sopenharmony_ci			default:
16462306a36Sopenharmony_ci				return RESPST_CHK_OP_VALID;
16562306a36Sopenharmony_ci			}
16662306a36Sopenharmony_ci		}
16762306a36Sopenharmony_ci		break;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	case IB_QPT_UC:
17062306a36Sopenharmony_ci		switch (qp->resp.opcode) {
17162306a36Sopenharmony_ci		case IB_OPCODE_UC_SEND_FIRST:
17262306a36Sopenharmony_ci		case IB_OPCODE_UC_SEND_MIDDLE:
17362306a36Sopenharmony_ci			switch (pkt->opcode) {
17462306a36Sopenharmony_ci			case IB_OPCODE_UC_SEND_MIDDLE:
17562306a36Sopenharmony_ci			case IB_OPCODE_UC_SEND_LAST:
17662306a36Sopenharmony_ci			case IB_OPCODE_UC_SEND_LAST_WITH_IMMEDIATE:
17762306a36Sopenharmony_ci				return RESPST_CHK_OP_VALID;
17862306a36Sopenharmony_ci			default:
17962306a36Sopenharmony_ci				return RESPST_ERR_MISSING_OPCODE_LAST_D1E;
18062306a36Sopenharmony_ci			}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		case IB_OPCODE_UC_RDMA_WRITE_FIRST:
18362306a36Sopenharmony_ci		case IB_OPCODE_UC_RDMA_WRITE_MIDDLE:
18462306a36Sopenharmony_ci			switch (pkt->opcode) {
18562306a36Sopenharmony_ci			case IB_OPCODE_UC_RDMA_WRITE_MIDDLE:
18662306a36Sopenharmony_ci			case IB_OPCODE_UC_RDMA_WRITE_LAST:
18762306a36Sopenharmony_ci			case IB_OPCODE_UC_RDMA_WRITE_LAST_WITH_IMMEDIATE:
18862306a36Sopenharmony_ci				return RESPST_CHK_OP_VALID;
18962306a36Sopenharmony_ci			default:
19062306a36Sopenharmony_ci				return RESPST_ERR_MISSING_OPCODE_LAST_D1E;
19162306a36Sopenharmony_ci			}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		default:
19462306a36Sopenharmony_ci			switch (pkt->opcode) {
19562306a36Sopenharmony_ci			case IB_OPCODE_UC_SEND_MIDDLE:
19662306a36Sopenharmony_ci			case IB_OPCODE_UC_SEND_LAST:
19762306a36Sopenharmony_ci			case IB_OPCODE_UC_SEND_LAST_WITH_IMMEDIATE:
19862306a36Sopenharmony_ci			case IB_OPCODE_UC_RDMA_WRITE_MIDDLE:
19962306a36Sopenharmony_ci			case IB_OPCODE_UC_RDMA_WRITE_LAST:
20062306a36Sopenharmony_ci			case IB_OPCODE_UC_RDMA_WRITE_LAST_WITH_IMMEDIATE:
20162306a36Sopenharmony_ci				qp->resp.drop_msg = 1;
20262306a36Sopenharmony_ci				return RESPST_CLEANUP;
20362306a36Sopenharmony_ci			default:
20462306a36Sopenharmony_ci				return RESPST_CHK_OP_VALID;
20562306a36Sopenharmony_ci			}
20662306a36Sopenharmony_ci		}
20762306a36Sopenharmony_ci		break;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	default:
21062306a36Sopenharmony_ci		return RESPST_CHK_OP_VALID;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic bool check_qp_attr_access(struct rxe_qp *qp,
21562306a36Sopenharmony_ci				 struct rxe_pkt_info *pkt)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	if (((pkt->mask & RXE_READ_MASK) &&
21862306a36Sopenharmony_ci	     !(qp->attr.qp_access_flags & IB_ACCESS_REMOTE_READ)) ||
21962306a36Sopenharmony_ci	    ((pkt->mask & (RXE_WRITE_MASK | RXE_ATOMIC_WRITE_MASK)) &&
22062306a36Sopenharmony_ci	     !(qp->attr.qp_access_flags & IB_ACCESS_REMOTE_WRITE)) ||
22162306a36Sopenharmony_ci	    ((pkt->mask & RXE_ATOMIC_MASK) &&
22262306a36Sopenharmony_ci	     !(qp->attr.qp_access_flags & IB_ACCESS_REMOTE_ATOMIC)))
22362306a36Sopenharmony_ci		return false;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (pkt->mask & RXE_FLUSH_MASK) {
22662306a36Sopenharmony_ci		u32 flush_type = feth_plt(pkt);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		if ((flush_type & IB_FLUSH_GLOBAL &&
22962306a36Sopenharmony_ci		     !(qp->attr.qp_access_flags & IB_ACCESS_FLUSH_GLOBAL)) ||
23062306a36Sopenharmony_ci		    (flush_type & IB_FLUSH_PERSISTENT &&
23162306a36Sopenharmony_ci		     !(qp->attr.qp_access_flags & IB_ACCESS_FLUSH_PERSISTENT)))
23262306a36Sopenharmony_ci			return false;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return true;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic enum resp_states check_op_valid(struct rxe_qp *qp,
23962306a36Sopenharmony_ci				       struct rxe_pkt_info *pkt)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	switch (qp_type(qp)) {
24262306a36Sopenharmony_ci	case IB_QPT_RC:
24362306a36Sopenharmony_ci		if (!check_qp_attr_access(qp, pkt))
24462306a36Sopenharmony_ci			return RESPST_ERR_UNSUPPORTED_OPCODE;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci		break;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	case IB_QPT_UC:
24962306a36Sopenharmony_ci		if ((pkt->mask & RXE_WRITE_MASK) &&
25062306a36Sopenharmony_ci		    !(qp->attr.qp_access_flags & IB_ACCESS_REMOTE_WRITE)) {
25162306a36Sopenharmony_ci			qp->resp.drop_msg = 1;
25262306a36Sopenharmony_ci			return RESPST_CLEANUP;
25362306a36Sopenharmony_ci		}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		break;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	case IB_QPT_UD:
25862306a36Sopenharmony_ci	case IB_QPT_GSI:
25962306a36Sopenharmony_ci		break;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	default:
26262306a36Sopenharmony_ci		WARN_ON_ONCE(1);
26362306a36Sopenharmony_ci		break;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return RESPST_CHK_RESOURCE;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic enum resp_states get_srq_wqe(struct rxe_qp *qp)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct rxe_srq *srq = qp->srq;
27262306a36Sopenharmony_ci	struct rxe_queue *q = srq->rq.queue;
27362306a36Sopenharmony_ci	struct rxe_recv_wqe *wqe;
27462306a36Sopenharmony_ci	struct ib_event ev;
27562306a36Sopenharmony_ci	unsigned int count;
27662306a36Sopenharmony_ci	size_t size;
27762306a36Sopenharmony_ci	unsigned long flags;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (srq->error)
28062306a36Sopenharmony_ci		return RESPST_ERR_RNR;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	spin_lock_irqsave(&srq->rq.consumer_lock, flags);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	wqe = queue_head(q, QUEUE_TYPE_FROM_CLIENT);
28562306a36Sopenharmony_ci	if (!wqe) {
28662306a36Sopenharmony_ci		spin_unlock_irqrestore(&srq->rq.consumer_lock, flags);
28762306a36Sopenharmony_ci		return RESPST_ERR_RNR;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* don't trust user space data */
29162306a36Sopenharmony_ci	if (unlikely(wqe->dma.num_sge > srq->rq.max_sge)) {
29262306a36Sopenharmony_ci		spin_unlock_irqrestore(&srq->rq.consumer_lock, flags);
29362306a36Sopenharmony_ci		rxe_dbg_qp(qp, "invalid num_sge in SRQ entry\n");
29462306a36Sopenharmony_ci		return RESPST_ERR_MALFORMED_WQE;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci	size = sizeof(*wqe) + wqe->dma.num_sge*sizeof(struct rxe_sge);
29762306a36Sopenharmony_ci	memcpy(&qp->resp.srq_wqe, wqe, size);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	qp->resp.wqe = &qp->resp.srq_wqe.wqe;
30062306a36Sopenharmony_ci	queue_advance_consumer(q, QUEUE_TYPE_FROM_CLIENT);
30162306a36Sopenharmony_ci	count = queue_count(q, QUEUE_TYPE_FROM_CLIENT);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (srq->limit && srq->ibsrq.event_handler && (count < srq->limit)) {
30462306a36Sopenharmony_ci		srq->limit = 0;
30562306a36Sopenharmony_ci		goto event;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	spin_unlock_irqrestore(&srq->rq.consumer_lock, flags);
30962306a36Sopenharmony_ci	return RESPST_CHK_LENGTH;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cievent:
31262306a36Sopenharmony_ci	spin_unlock_irqrestore(&srq->rq.consumer_lock, flags);
31362306a36Sopenharmony_ci	ev.device = qp->ibqp.device;
31462306a36Sopenharmony_ci	ev.element.srq = qp->ibqp.srq;
31562306a36Sopenharmony_ci	ev.event = IB_EVENT_SRQ_LIMIT_REACHED;
31662306a36Sopenharmony_ci	srq->ibsrq.event_handler(&ev, srq->ibsrq.srq_context);
31762306a36Sopenharmony_ci	return RESPST_CHK_LENGTH;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic enum resp_states check_resource(struct rxe_qp *qp,
32162306a36Sopenharmony_ci				       struct rxe_pkt_info *pkt)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct rxe_srq *srq = qp->srq;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (pkt->mask & (RXE_READ_OR_ATOMIC_MASK | RXE_ATOMIC_WRITE_MASK)) {
32662306a36Sopenharmony_ci		/* it is the requesters job to not send
32762306a36Sopenharmony_ci		 * too many read/atomic ops, we just
32862306a36Sopenharmony_ci		 * recycle the responder resource queue
32962306a36Sopenharmony_ci		 */
33062306a36Sopenharmony_ci		if (likely(qp->attr.max_dest_rd_atomic > 0))
33162306a36Sopenharmony_ci			return RESPST_CHK_LENGTH;
33262306a36Sopenharmony_ci		else
33362306a36Sopenharmony_ci			return RESPST_ERR_TOO_MANY_RDMA_ATM_REQ;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (pkt->mask & RXE_RWR_MASK) {
33762306a36Sopenharmony_ci		if (srq)
33862306a36Sopenharmony_ci			return get_srq_wqe(qp);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		qp->resp.wqe = queue_head(qp->rq.queue,
34162306a36Sopenharmony_ci				QUEUE_TYPE_FROM_CLIENT);
34262306a36Sopenharmony_ci		return (qp->resp.wqe) ? RESPST_CHK_LENGTH : RESPST_ERR_RNR;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return RESPST_CHK_LENGTH;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic enum resp_states rxe_resp_check_length(struct rxe_qp *qp,
34962306a36Sopenharmony_ci					      struct rxe_pkt_info *pkt)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	/*
35262306a36Sopenharmony_ci	 * See IBA C9-92
35362306a36Sopenharmony_ci	 * For UD QPs we only check if the packet will fit in the
35462306a36Sopenharmony_ci	 * receive buffer later. For rmda operations additional
35562306a36Sopenharmony_ci	 * length checks are performed in check_rkey.
35662306a36Sopenharmony_ci	 */
35762306a36Sopenharmony_ci	if (pkt->mask & RXE_PAYLOAD_MASK && ((qp_type(qp) == IB_QPT_RC) ||
35862306a36Sopenharmony_ci					     (qp_type(qp) == IB_QPT_UC))) {
35962306a36Sopenharmony_ci		unsigned int mtu = qp->mtu;
36062306a36Sopenharmony_ci		unsigned int payload = payload_size(pkt);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		if ((pkt->mask & RXE_START_MASK) &&
36362306a36Sopenharmony_ci		    (pkt->mask & RXE_END_MASK)) {
36462306a36Sopenharmony_ci			if (unlikely(payload > mtu)) {
36562306a36Sopenharmony_ci				rxe_dbg_qp(qp, "only packet too long");
36662306a36Sopenharmony_ci				return RESPST_ERR_LENGTH;
36762306a36Sopenharmony_ci			}
36862306a36Sopenharmony_ci		} else if ((pkt->mask & RXE_START_MASK) ||
36962306a36Sopenharmony_ci			   (pkt->mask & RXE_MIDDLE_MASK)) {
37062306a36Sopenharmony_ci			if (unlikely(payload != mtu)) {
37162306a36Sopenharmony_ci				rxe_dbg_qp(qp, "first or middle packet not mtu");
37262306a36Sopenharmony_ci				return RESPST_ERR_LENGTH;
37362306a36Sopenharmony_ci			}
37462306a36Sopenharmony_ci		} else if (pkt->mask & RXE_END_MASK) {
37562306a36Sopenharmony_ci			if (unlikely((payload == 0) || (payload > mtu))) {
37662306a36Sopenharmony_ci				rxe_dbg_qp(qp, "last packet zero or too long");
37762306a36Sopenharmony_ci				return RESPST_ERR_LENGTH;
37862306a36Sopenharmony_ci			}
37962306a36Sopenharmony_ci		}
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	/* See IBA C9-94 */
38362306a36Sopenharmony_ci	if (pkt->mask & RXE_RETH_MASK) {
38462306a36Sopenharmony_ci		if (reth_len(pkt) > (1U << 31)) {
38562306a36Sopenharmony_ci			rxe_dbg_qp(qp, "dma length too long");
38662306a36Sopenharmony_ci			return RESPST_ERR_LENGTH;
38762306a36Sopenharmony_ci		}
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (pkt->mask & RXE_RDMA_OP_MASK)
39162306a36Sopenharmony_ci		return RESPST_CHK_RKEY;
39262306a36Sopenharmony_ci	else
39362306a36Sopenharmony_ci		return RESPST_EXECUTE;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/* if the reth length field is zero we can assume nothing
39762306a36Sopenharmony_ci * about the rkey value and should not validate or use it.
39862306a36Sopenharmony_ci * Instead set qp->resp.rkey to 0 which is an invalid rkey
39962306a36Sopenharmony_ci * value since the minimum index part is 1.
40062306a36Sopenharmony_ci */
40162306a36Sopenharmony_cistatic void qp_resp_from_reth(struct rxe_qp *qp, struct rxe_pkt_info *pkt)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	unsigned int length = reth_len(pkt);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	qp->resp.va = reth_va(pkt);
40662306a36Sopenharmony_ci	qp->resp.offset = 0;
40762306a36Sopenharmony_ci	qp->resp.resid = length;
40862306a36Sopenharmony_ci	qp->resp.length = length;
40962306a36Sopenharmony_ci	if (pkt->mask & RXE_READ_OR_WRITE_MASK && length == 0)
41062306a36Sopenharmony_ci		qp->resp.rkey = 0;
41162306a36Sopenharmony_ci	else
41262306a36Sopenharmony_ci		qp->resp.rkey = reth_rkey(pkt);
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic void qp_resp_from_atmeth(struct rxe_qp *qp, struct rxe_pkt_info *pkt)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	qp->resp.va = atmeth_va(pkt);
41862306a36Sopenharmony_ci	qp->resp.offset = 0;
41962306a36Sopenharmony_ci	qp->resp.rkey = atmeth_rkey(pkt);
42062306a36Sopenharmony_ci	qp->resp.resid = sizeof(u64);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci/* resolve the packet rkey to qp->resp.mr or set qp->resp.mr to NULL
42462306a36Sopenharmony_ci * if an invalid rkey is received or the rdma length is zero. For middle
42562306a36Sopenharmony_ci * or last packets use the stored value of mr.
42662306a36Sopenharmony_ci */
42762306a36Sopenharmony_cistatic enum resp_states check_rkey(struct rxe_qp *qp,
42862306a36Sopenharmony_ci				   struct rxe_pkt_info *pkt)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	struct rxe_mr *mr = NULL;
43162306a36Sopenharmony_ci	struct rxe_mw *mw = NULL;
43262306a36Sopenharmony_ci	u64 va;
43362306a36Sopenharmony_ci	u32 rkey;
43462306a36Sopenharmony_ci	u32 resid;
43562306a36Sopenharmony_ci	u32 pktlen;
43662306a36Sopenharmony_ci	int mtu = qp->mtu;
43762306a36Sopenharmony_ci	enum resp_states state;
43862306a36Sopenharmony_ci	int access = 0;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/* parse RETH or ATMETH header for first/only packets
44162306a36Sopenharmony_ci	 * for va, length, rkey, etc. or use current value for
44262306a36Sopenharmony_ci	 * middle/last packets.
44362306a36Sopenharmony_ci	 */
44462306a36Sopenharmony_ci	if (pkt->mask & (RXE_READ_OR_WRITE_MASK | RXE_ATOMIC_WRITE_MASK)) {
44562306a36Sopenharmony_ci		if (pkt->mask & RXE_RETH_MASK)
44662306a36Sopenharmony_ci			qp_resp_from_reth(qp, pkt);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		access = (pkt->mask & RXE_READ_MASK) ? IB_ACCESS_REMOTE_READ
44962306a36Sopenharmony_ci						     : IB_ACCESS_REMOTE_WRITE;
45062306a36Sopenharmony_ci	} else if (pkt->mask & RXE_FLUSH_MASK) {
45162306a36Sopenharmony_ci		u32 flush_type = feth_plt(pkt);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		if (pkt->mask & RXE_RETH_MASK)
45462306a36Sopenharmony_ci			qp_resp_from_reth(qp, pkt);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		if (flush_type & IB_FLUSH_GLOBAL)
45762306a36Sopenharmony_ci			access |= IB_ACCESS_FLUSH_GLOBAL;
45862306a36Sopenharmony_ci		if (flush_type & IB_FLUSH_PERSISTENT)
45962306a36Sopenharmony_ci			access |= IB_ACCESS_FLUSH_PERSISTENT;
46062306a36Sopenharmony_ci	} else if (pkt->mask & RXE_ATOMIC_MASK) {
46162306a36Sopenharmony_ci		qp_resp_from_atmeth(qp, pkt);
46262306a36Sopenharmony_ci		access = IB_ACCESS_REMOTE_ATOMIC;
46362306a36Sopenharmony_ci	} else {
46462306a36Sopenharmony_ci		/* shouldn't happen */
46562306a36Sopenharmony_ci		WARN_ON(1);
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/* A zero-byte read or write op is not required to
46962306a36Sopenharmony_ci	 * set an addr or rkey. See C9-88
47062306a36Sopenharmony_ci	 */
47162306a36Sopenharmony_ci	if ((pkt->mask & RXE_READ_OR_WRITE_MASK) &&
47262306a36Sopenharmony_ci	    (pkt->mask & RXE_RETH_MASK) && reth_len(pkt) == 0) {
47362306a36Sopenharmony_ci		qp->resp.mr = NULL;
47462306a36Sopenharmony_ci		return RESPST_EXECUTE;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	va	= qp->resp.va;
47862306a36Sopenharmony_ci	rkey	= qp->resp.rkey;
47962306a36Sopenharmony_ci	resid	= qp->resp.resid;
48062306a36Sopenharmony_ci	pktlen	= payload_size(pkt);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (rkey_is_mw(rkey)) {
48362306a36Sopenharmony_ci		mw = rxe_lookup_mw(qp, access, rkey);
48462306a36Sopenharmony_ci		if (!mw) {
48562306a36Sopenharmony_ci			rxe_dbg_qp(qp, "no MW matches rkey %#x\n", rkey);
48662306a36Sopenharmony_ci			state = RESPST_ERR_RKEY_VIOLATION;
48762306a36Sopenharmony_ci			goto err;
48862306a36Sopenharmony_ci		}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		mr = mw->mr;
49162306a36Sopenharmony_ci		if (!mr) {
49262306a36Sopenharmony_ci			rxe_dbg_qp(qp, "MW doesn't have an MR\n");
49362306a36Sopenharmony_ci			state = RESPST_ERR_RKEY_VIOLATION;
49462306a36Sopenharmony_ci			goto err;
49562306a36Sopenharmony_ci		}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		if (mw->access & IB_ZERO_BASED)
49862306a36Sopenharmony_ci			qp->resp.offset = mw->addr;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		rxe_get(mr);
50162306a36Sopenharmony_ci		rxe_put(mw);
50262306a36Sopenharmony_ci		mw = NULL;
50362306a36Sopenharmony_ci	} else {
50462306a36Sopenharmony_ci		mr = lookup_mr(qp->pd, access, rkey, RXE_LOOKUP_REMOTE);
50562306a36Sopenharmony_ci		if (!mr) {
50662306a36Sopenharmony_ci			rxe_dbg_qp(qp, "no MR matches rkey %#x\n", rkey);
50762306a36Sopenharmony_ci			state = RESPST_ERR_RKEY_VIOLATION;
50862306a36Sopenharmony_ci			goto err;
50962306a36Sopenharmony_ci		}
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (pkt->mask & RXE_FLUSH_MASK) {
51362306a36Sopenharmony_ci		/* FLUSH MR may not set va or resid
51462306a36Sopenharmony_ci		 * no need to check range since we will flush whole mr
51562306a36Sopenharmony_ci		 */
51662306a36Sopenharmony_ci		if (feth_sel(pkt) == IB_FLUSH_MR)
51762306a36Sopenharmony_ci			goto skip_check_range;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (mr_check_range(mr, va + qp->resp.offset, resid)) {
52162306a36Sopenharmony_ci		state = RESPST_ERR_RKEY_VIOLATION;
52262306a36Sopenharmony_ci		goto err;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ciskip_check_range:
52662306a36Sopenharmony_ci	if (pkt->mask & (RXE_WRITE_MASK | RXE_ATOMIC_WRITE_MASK)) {
52762306a36Sopenharmony_ci		if (resid > mtu) {
52862306a36Sopenharmony_ci			if (pktlen != mtu || bth_pad(pkt)) {
52962306a36Sopenharmony_ci				state = RESPST_ERR_LENGTH;
53062306a36Sopenharmony_ci				goto err;
53162306a36Sopenharmony_ci			}
53262306a36Sopenharmony_ci		} else {
53362306a36Sopenharmony_ci			if (pktlen != resid) {
53462306a36Sopenharmony_ci				state = RESPST_ERR_LENGTH;
53562306a36Sopenharmony_ci				goto err;
53662306a36Sopenharmony_ci			}
53762306a36Sopenharmony_ci			if ((bth_pad(pkt) != (0x3 & (-resid)))) {
53862306a36Sopenharmony_ci				/* This case may not be exactly that
53962306a36Sopenharmony_ci				 * but nothing else fits.
54062306a36Sopenharmony_ci				 */
54162306a36Sopenharmony_ci				state = RESPST_ERR_LENGTH;
54262306a36Sopenharmony_ci				goto err;
54362306a36Sopenharmony_ci			}
54462306a36Sopenharmony_ci		}
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	WARN_ON_ONCE(qp->resp.mr);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	qp->resp.mr = mr;
55062306a36Sopenharmony_ci	return RESPST_EXECUTE;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cierr:
55362306a36Sopenharmony_ci	qp->resp.mr = NULL;
55462306a36Sopenharmony_ci	if (mr)
55562306a36Sopenharmony_ci		rxe_put(mr);
55662306a36Sopenharmony_ci	if (mw)
55762306a36Sopenharmony_ci		rxe_put(mw);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return state;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic enum resp_states send_data_in(struct rxe_qp *qp, void *data_addr,
56362306a36Sopenharmony_ci				     int data_len)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	int err;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	err = copy_data(qp->pd, IB_ACCESS_LOCAL_WRITE, &qp->resp.wqe->dma,
56862306a36Sopenharmony_ci			data_addr, data_len, RXE_TO_MR_OBJ);
56962306a36Sopenharmony_ci	if (unlikely(err))
57062306a36Sopenharmony_ci		return (err == -ENOSPC) ? RESPST_ERR_LENGTH
57162306a36Sopenharmony_ci					: RESPST_ERR_MALFORMED_WQE;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	return RESPST_NONE;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic enum resp_states write_data_in(struct rxe_qp *qp,
57762306a36Sopenharmony_ci				      struct rxe_pkt_info *pkt)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	enum resp_states rc = RESPST_NONE;
58062306a36Sopenharmony_ci	int	err;
58162306a36Sopenharmony_ci	int data_len = payload_size(pkt);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	err = rxe_mr_copy(qp->resp.mr, qp->resp.va + qp->resp.offset,
58462306a36Sopenharmony_ci			  payload_addr(pkt), data_len, RXE_TO_MR_OBJ);
58562306a36Sopenharmony_ci	if (err) {
58662306a36Sopenharmony_ci		rc = RESPST_ERR_RKEY_VIOLATION;
58762306a36Sopenharmony_ci		goto out;
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	qp->resp.va += data_len;
59162306a36Sopenharmony_ci	qp->resp.resid -= data_len;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ciout:
59462306a36Sopenharmony_ci	return rc;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic struct resp_res *rxe_prepare_res(struct rxe_qp *qp,
59862306a36Sopenharmony_ci					struct rxe_pkt_info *pkt,
59962306a36Sopenharmony_ci					int type)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct resp_res *res;
60262306a36Sopenharmony_ci	u32 pkts;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	res = &qp->resp.resources[qp->resp.res_head];
60562306a36Sopenharmony_ci	rxe_advance_resp_resource(qp);
60662306a36Sopenharmony_ci	free_rd_atomic_resource(res);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	res->type = type;
60962306a36Sopenharmony_ci	res->replay = 0;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	switch (type) {
61262306a36Sopenharmony_ci	case RXE_READ_MASK:
61362306a36Sopenharmony_ci		res->read.va = qp->resp.va + qp->resp.offset;
61462306a36Sopenharmony_ci		res->read.va_org = qp->resp.va + qp->resp.offset;
61562306a36Sopenharmony_ci		res->read.resid = qp->resp.resid;
61662306a36Sopenharmony_ci		res->read.length = qp->resp.resid;
61762306a36Sopenharmony_ci		res->read.rkey = qp->resp.rkey;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		pkts = max_t(u32, (reth_len(pkt) + qp->mtu - 1)/qp->mtu, 1);
62062306a36Sopenharmony_ci		res->first_psn = pkt->psn;
62162306a36Sopenharmony_ci		res->cur_psn = pkt->psn;
62262306a36Sopenharmony_ci		res->last_psn = (pkt->psn + pkts - 1) & BTH_PSN_MASK;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		res->state = rdatm_res_state_new;
62562306a36Sopenharmony_ci		break;
62662306a36Sopenharmony_ci	case RXE_ATOMIC_MASK:
62762306a36Sopenharmony_ci	case RXE_ATOMIC_WRITE_MASK:
62862306a36Sopenharmony_ci		res->first_psn = pkt->psn;
62962306a36Sopenharmony_ci		res->last_psn = pkt->psn;
63062306a36Sopenharmony_ci		res->cur_psn = pkt->psn;
63162306a36Sopenharmony_ci		break;
63262306a36Sopenharmony_ci	case RXE_FLUSH_MASK:
63362306a36Sopenharmony_ci		res->flush.va = qp->resp.va + qp->resp.offset;
63462306a36Sopenharmony_ci		res->flush.length = qp->resp.length;
63562306a36Sopenharmony_ci		res->flush.type = feth_plt(pkt);
63662306a36Sopenharmony_ci		res->flush.level = feth_sel(pkt);
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return res;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic enum resp_states process_flush(struct rxe_qp *qp,
64362306a36Sopenharmony_ci				       struct rxe_pkt_info *pkt)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	u64 length, start;
64662306a36Sopenharmony_ci	struct rxe_mr *mr = qp->resp.mr;
64762306a36Sopenharmony_ci	struct resp_res *res = qp->resp.res;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	/* oA19-14, oA19-15 */
65062306a36Sopenharmony_ci	if (res && res->replay)
65162306a36Sopenharmony_ci		return RESPST_ACKNOWLEDGE;
65262306a36Sopenharmony_ci	else if (!res) {
65362306a36Sopenharmony_ci		res = rxe_prepare_res(qp, pkt, RXE_FLUSH_MASK);
65462306a36Sopenharmony_ci		qp->resp.res = res;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	if (res->flush.level == IB_FLUSH_RANGE) {
65862306a36Sopenharmony_ci		start = res->flush.va;
65962306a36Sopenharmony_ci		length = res->flush.length;
66062306a36Sopenharmony_ci	} else { /* level == IB_FLUSH_MR */
66162306a36Sopenharmony_ci		start = mr->ibmr.iova;
66262306a36Sopenharmony_ci		length = mr->ibmr.length;
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (res->flush.type & IB_FLUSH_PERSISTENT) {
66662306a36Sopenharmony_ci		if (rxe_flush_pmem_iova(mr, start, length))
66762306a36Sopenharmony_ci			return RESPST_ERR_RKEY_VIOLATION;
66862306a36Sopenharmony_ci		/* Make data persistent. */
66962306a36Sopenharmony_ci		wmb();
67062306a36Sopenharmony_ci	} else if (res->flush.type & IB_FLUSH_GLOBAL) {
67162306a36Sopenharmony_ci		/* Make data global visibility. */
67262306a36Sopenharmony_ci		wmb();
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	qp->resp.msn++;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* next expected psn, read handles this separately */
67862306a36Sopenharmony_ci	qp->resp.psn = (pkt->psn + 1) & BTH_PSN_MASK;
67962306a36Sopenharmony_ci	qp->resp.ack_psn = qp->resp.psn;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	qp->resp.opcode = pkt->opcode;
68262306a36Sopenharmony_ci	qp->resp.status = IB_WC_SUCCESS;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	return RESPST_ACKNOWLEDGE;
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic enum resp_states atomic_reply(struct rxe_qp *qp,
68862306a36Sopenharmony_ci				     struct rxe_pkt_info *pkt)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	struct rxe_mr *mr = qp->resp.mr;
69162306a36Sopenharmony_ci	struct resp_res *res = qp->resp.res;
69262306a36Sopenharmony_ci	int err;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (!res) {
69562306a36Sopenharmony_ci		res = rxe_prepare_res(qp, pkt, RXE_ATOMIC_MASK);
69662306a36Sopenharmony_ci		qp->resp.res = res;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	if (!res->replay) {
70062306a36Sopenharmony_ci		u64 iova = qp->resp.va + qp->resp.offset;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		err = rxe_mr_do_atomic_op(mr, iova, pkt->opcode,
70362306a36Sopenharmony_ci					  atmeth_comp(pkt),
70462306a36Sopenharmony_ci					  atmeth_swap_add(pkt),
70562306a36Sopenharmony_ci					  &res->atomic.orig_val);
70662306a36Sopenharmony_ci		if (err)
70762306a36Sopenharmony_ci			return err;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		qp->resp.msn++;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci		/* next expected psn, read handles this separately */
71262306a36Sopenharmony_ci		qp->resp.psn = (pkt->psn + 1) & BTH_PSN_MASK;
71362306a36Sopenharmony_ci		qp->resp.ack_psn = qp->resp.psn;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		qp->resp.opcode = pkt->opcode;
71662306a36Sopenharmony_ci		qp->resp.status = IB_WC_SUCCESS;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	return RESPST_ACKNOWLEDGE;
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic enum resp_states atomic_write_reply(struct rxe_qp *qp,
72362306a36Sopenharmony_ci					   struct rxe_pkt_info *pkt)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	struct resp_res *res = qp->resp.res;
72662306a36Sopenharmony_ci	struct rxe_mr *mr;
72762306a36Sopenharmony_ci	u64 value;
72862306a36Sopenharmony_ci	u64 iova;
72962306a36Sopenharmony_ci	int err;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (!res) {
73262306a36Sopenharmony_ci		res = rxe_prepare_res(qp, pkt, RXE_ATOMIC_WRITE_MASK);
73362306a36Sopenharmony_ci		qp->resp.res = res;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (res->replay)
73762306a36Sopenharmony_ci		return RESPST_ACKNOWLEDGE;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	mr = qp->resp.mr;
74062306a36Sopenharmony_ci	value = *(u64 *)payload_addr(pkt);
74162306a36Sopenharmony_ci	iova = qp->resp.va + qp->resp.offset;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	err = rxe_mr_do_atomic_write(mr, iova, value);
74462306a36Sopenharmony_ci	if (err)
74562306a36Sopenharmony_ci		return err;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	qp->resp.resid = 0;
74862306a36Sopenharmony_ci	qp->resp.msn++;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	/* next expected psn, read handles this separately */
75162306a36Sopenharmony_ci	qp->resp.psn = (pkt->psn + 1) & BTH_PSN_MASK;
75262306a36Sopenharmony_ci	qp->resp.ack_psn = qp->resp.psn;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	qp->resp.opcode = pkt->opcode;
75562306a36Sopenharmony_ci	qp->resp.status = IB_WC_SUCCESS;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	return RESPST_ACKNOWLEDGE;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic struct sk_buff *prepare_ack_packet(struct rxe_qp *qp,
76162306a36Sopenharmony_ci					  struct rxe_pkt_info *ack,
76262306a36Sopenharmony_ci					  int opcode,
76362306a36Sopenharmony_ci					  int payload,
76462306a36Sopenharmony_ci					  u32 psn,
76562306a36Sopenharmony_ci					  u8 syndrome)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
76862306a36Sopenharmony_ci	struct sk_buff *skb;
76962306a36Sopenharmony_ci	int paylen;
77062306a36Sopenharmony_ci	int pad;
77162306a36Sopenharmony_ci	int err;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	/*
77462306a36Sopenharmony_ci	 * allocate packet
77562306a36Sopenharmony_ci	 */
77662306a36Sopenharmony_ci	pad = (-payload) & 0x3;
77762306a36Sopenharmony_ci	paylen = rxe_opcode[opcode].length + payload + pad + RXE_ICRC_SIZE;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	skb = rxe_init_packet(rxe, &qp->pri_av, paylen, ack);
78062306a36Sopenharmony_ci	if (!skb)
78162306a36Sopenharmony_ci		return NULL;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	ack->qp = qp;
78462306a36Sopenharmony_ci	ack->opcode = opcode;
78562306a36Sopenharmony_ci	ack->mask = rxe_opcode[opcode].mask;
78662306a36Sopenharmony_ci	ack->paylen = paylen;
78762306a36Sopenharmony_ci	ack->psn = psn;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	bth_init(ack, opcode, 0, 0, pad, IB_DEFAULT_PKEY_FULL,
79062306a36Sopenharmony_ci		 qp->attr.dest_qp_num, 0, psn);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	if (ack->mask & RXE_AETH_MASK) {
79362306a36Sopenharmony_ci		aeth_set_syn(ack, syndrome);
79462306a36Sopenharmony_ci		aeth_set_msn(ack, qp->resp.msn);
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	if (ack->mask & RXE_ATMACK_MASK)
79862306a36Sopenharmony_ci		atmack_set_orig(ack, qp->resp.res->atomic.orig_val);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	err = rxe_prepare(&qp->pri_av, ack, skb);
80162306a36Sopenharmony_ci	if (err) {
80262306a36Sopenharmony_ci		kfree_skb(skb);
80362306a36Sopenharmony_ci		return NULL;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	return skb;
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci/**
81062306a36Sopenharmony_ci * rxe_recheck_mr - revalidate MR from rkey and get a reference
81162306a36Sopenharmony_ci * @qp: the qp
81262306a36Sopenharmony_ci * @rkey: the rkey
81362306a36Sopenharmony_ci *
81462306a36Sopenharmony_ci * This code allows the MR to be invalidated or deregistered or
81562306a36Sopenharmony_ci * the MW if one was used to be invalidated or deallocated.
81662306a36Sopenharmony_ci * It is assumed that the access permissions if originally good
81762306a36Sopenharmony_ci * are OK and the mappings to be unchanged.
81862306a36Sopenharmony_ci *
81962306a36Sopenharmony_ci * TODO: If someone reregisters an MR to change its size or
82062306a36Sopenharmony_ci * access permissions during the processing of an RDMA read
82162306a36Sopenharmony_ci * we should kill the responder resource and complete the
82262306a36Sopenharmony_ci * operation with an error.
82362306a36Sopenharmony_ci *
82462306a36Sopenharmony_ci * Return: mr on success else NULL
82562306a36Sopenharmony_ci */
82662306a36Sopenharmony_cistatic struct rxe_mr *rxe_recheck_mr(struct rxe_qp *qp, u32 rkey)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
82962306a36Sopenharmony_ci	struct rxe_mr *mr;
83062306a36Sopenharmony_ci	struct rxe_mw *mw;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (rkey_is_mw(rkey)) {
83362306a36Sopenharmony_ci		mw = rxe_pool_get_index(&rxe->mw_pool, rkey >> 8);
83462306a36Sopenharmony_ci		if (!mw)
83562306a36Sopenharmony_ci			return NULL;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci		mr = mw->mr;
83862306a36Sopenharmony_ci		if (mw->rkey != rkey || mw->state != RXE_MW_STATE_VALID ||
83962306a36Sopenharmony_ci		    !mr || mr->state != RXE_MR_STATE_VALID) {
84062306a36Sopenharmony_ci			rxe_put(mw);
84162306a36Sopenharmony_ci			return NULL;
84262306a36Sopenharmony_ci		}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		rxe_get(mr);
84562306a36Sopenharmony_ci		rxe_put(mw);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci		return mr;
84862306a36Sopenharmony_ci	}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	mr = rxe_pool_get_index(&rxe->mr_pool, rkey >> 8);
85162306a36Sopenharmony_ci	if (!mr)
85262306a36Sopenharmony_ci		return NULL;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	if (mr->rkey != rkey || mr->state != RXE_MR_STATE_VALID) {
85562306a36Sopenharmony_ci		rxe_put(mr);
85662306a36Sopenharmony_ci		return NULL;
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	return mr;
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci/* RDMA read response. If res is not NULL, then we have a current RDMA request
86362306a36Sopenharmony_ci * being processed or replayed.
86462306a36Sopenharmony_ci */
86562306a36Sopenharmony_cistatic enum resp_states read_reply(struct rxe_qp *qp,
86662306a36Sopenharmony_ci				   struct rxe_pkt_info *req_pkt)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	struct rxe_pkt_info ack_pkt;
86962306a36Sopenharmony_ci	struct sk_buff *skb;
87062306a36Sopenharmony_ci	int mtu = qp->mtu;
87162306a36Sopenharmony_ci	enum resp_states state;
87262306a36Sopenharmony_ci	int payload;
87362306a36Sopenharmony_ci	int opcode;
87462306a36Sopenharmony_ci	int err;
87562306a36Sopenharmony_ci	struct resp_res *res = qp->resp.res;
87662306a36Sopenharmony_ci	struct rxe_mr *mr;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (!res) {
87962306a36Sopenharmony_ci		res = rxe_prepare_res(qp, req_pkt, RXE_READ_MASK);
88062306a36Sopenharmony_ci		qp->resp.res = res;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	if (res->state == rdatm_res_state_new) {
88462306a36Sopenharmony_ci		if (!res->replay || qp->resp.length == 0) {
88562306a36Sopenharmony_ci			/* if length == 0 mr will be NULL (is ok)
88662306a36Sopenharmony_ci			 * otherwise qp->resp.mr holds a ref on mr
88762306a36Sopenharmony_ci			 * which we transfer to mr and drop below.
88862306a36Sopenharmony_ci			 */
88962306a36Sopenharmony_ci			mr = qp->resp.mr;
89062306a36Sopenharmony_ci			qp->resp.mr = NULL;
89162306a36Sopenharmony_ci		} else {
89262306a36Sopenharmony_ci			mr = rxe_recheck_mr(qp, res->read.rkey);
89362306a36Sopenharmony_ci			if (!mr)
89462306a36Sopenharmony_ci				return RESPST_ERR_RKEY_VIOLATION;
89562306a36Sopenharmony_ci		}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci		if (res->read.resid <= mtu)
89862306a36Sopenharmony_ci			opcode = IB_OPCODE_RC_RDMA_READ_RESPONSE_ONLY;
89962306a36Sopenharmony_ci		else
90062306a36Sopenharmony_ci			opcode = IB_OPCODE_RC_RDMA_READ_RESPONSE_FIRST;
90162306a36Sopenharmony_ci	} else {
90262306a36Sopenharmony_ci		/* re-lookup mr from rkey on all later packets.
90362306a36Sopenharmony_ci		 * length will be non-zero. This can fail if someone
90462306a36Sopenharmony_ci		 * modifies or destroys the mr since the first packet.
90562306a36Sopenharmony_ci		 */
90662306a36Sopenharmony_ci		mr = rxe_recheck_mr(qp, res->read.rkey);
90762306a36Sopenharmony_ci		if (!mr)
90862306a36Sopenharmony_ci			return RESPST_ERR_RKEY_VIOLATION;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci		if (res->read.resid > mtu)
91162306a36Sopenharmony_ci			opcode = IB_OPCODE_RC_RDMA_READ_RESPONSE_MIDDLE;
91262306a36Sopenharmony_ci		else
91362306a36Sopenharmony_ci			opcode = IB_OPCODE_RC_RDMA_READ_RESPONSE_LAST;
91462306a36Sopenharmony_ci	}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	res->state = rdatm_res_state_next;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	payload = min_t(int, res->read.resid, mtu);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	skb = prepare_ack_packet(qp, &ack_pkt, opcode, payload,
92162306a36Sopenharmony_ci				 res->cur_psn, AETH_ACK_UNLIMITED);
92262306a36Sopenharmony_ci	if (!skb) {
92362306a36Sopenharmony_ci		state = RESPST_ERR_RNR;
92462306a36Sopenharmony_ci		goto err_out;
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	err = rxe_mr_copy(mr, res->read.va, payload_addr(&ack_pkt),
92862306a36Sopenharmony_ci			  payload, RXE_FROM_MR_OBJ);
92962306a36Sopenharmony_ci	if (err) {
93062306a36Sopenharmony_ci		kfree_skb(skb);
93162306a36Sopenharmony_ci		state = RESPST_ERR_RKEY_VIOLATION;
93262306a36Sopenharmony_ci		goto err_out;
93362306a36Sopenharmony_ci	}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	if (bth_pad(&ack_pkt)) {
93662306a36Sopenharmony_ci		u8 *pad = payload_addr(&ack_pkt) + payload;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci		memset(pad, 0, bth_pad(&ack_pkt));
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	/* rxe_xmit_packet always consumes the skb */
94262306a36Sopenharmony_ci	err = rxe_xmit_packet(qp, &ack_pkt, skb);
94362306a36Sopenharmony_ci	if (err) {
94462306a36Sopenharmony_ci		state = RESPST_ERR_RNR;
94562306a36Sopenharmony_ci		goto err_out;
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	res->read.va += payload;
94962306a36Sopenharmony_ci	res->read.resid -= payload;
95062306a36Sopenharmony_ci	res->cur_psn = (res->cur_psn + 1) & BTH_PSN_MASK;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	if (res->read.resid > 0) {
95362306a36Sopenharmony_ci		state = RESPST_DONE;
95462306a36Sopenharmony_ci	} else {
95562306a36Sopenharmony_ci		qp->resp.res = NULL;
95662306a36Sopenharmony_ci		if (!res->replay)
95762306a36Sopenharmony_ci			qp->resp.opcode = -1;
95862306a36Sopenharmony_ci		if (psn_compare(res->cur_psn, qp->resp.psn) >= 0)
95962306a36Sopenharmony_ci			qp->resp.psn = res->cur_psn;
96062306a36Sopenharmony_ci		state = RESPST_CLEANUP;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_cierr_out:
96462306a36Sopenharmony_ci	if (mr)
96562306a36Sopenharmony_ci		rxe_put(mr);
96662306a36Sopenharmony_ci	return state;
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic int invalidate_rkey(struct rxe_qp *qp, u32 rkey)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	if (rkey_is_mw(rkey))
97262306a36Sopenharmony_ci		return rxe_invalidate_mw(qp, rkey);
97362306a36Sopenharmony_ci	else
97462306a36Sopenharmony_ci		return rxe_invalidate_mr(qp, rkey);
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci/* Executes a new request. A retried request never reach that function (send
97862306a36Sopenharmony_ci * and writes are discarded, and reads and atomics are retried elsewhere.
97962306a36Sopenharmony_ci */
98062306a36Sopenharmony_cistatic enum resp_states execute(struct rxe_qp *qp, struct rxe_pkt_info *pkt)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	enum resp_states err;
98362306a36Sopenharmony_ci	struct sk_buff *skb = PKT_TO_SKB(pkt);
98462306a36Sopenharmony_ci	union rdma_network_hdr hdr;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	if (pkt->mask & RXE_SEND_MASK) {
98762306a36Sopenharmony_ci		if (qp_type(qp) == IB_QPT_UD ||
98862306a36Sopenharmony_ci		    qp_type(qp) == IB_QPT_GSI) {
98962306a36Sopenharmony_ci			if (skb->protocol == htons(ETH_P_IP)) {
99062306a36Sopenharmony_ci				memset(&hdr.reserved, 0,
99162306a36Sopenharmony_ci						sizeof(hdr.reserved));
99262306a36Sopenharmony_ci				memcpy(&hdr.roce4grh, ip_hdr(skb),
99362306a36Sopenharmony_ci						sizeof(hdr.roce4grh));
99462306a36Sopenharmony_ci				err = send_data_in(qp, &hdr, sizeof(hdr));
99562306a36Sopenharmony_ci			} else {
99662306a36Sopenharmony_ci				err = send_data_in(qp, ipv6_hdr(skb),
99762306a36Sopenharmony_ci						sizeof(hdr));
99862306a36Sopenharmony_ci			}
99962306a36Sopenharmony_ci			if (err)
100062306a36Sopenharmony_ci				return err;
100162306a36Sopenharmony_ci		}
100262306a36Sopenharmony_ci		err = send_data_in(qp, payload_addr(pkt), payload_size(pkt));
100362306a36Sopenharmony_ci		if (err)
100462306a36Sopenharmony_ci			return err;
100562306a36Sopenharmony_ci	} else if (pkt->mask & RXE_WRITE_MASK) {
100662306a36Sopenharmony_ci		err = write_data_in(qp, pkt);
100762306a36Sopenharmony_ci		if (err)
100862306a36Sopenharmony_ci			return err;
100962306a36Sopenharmony_ci	} else if (pkt->mask & RXE_READ_MASK) {
101062306a36Sopenharmony_ci		/* For RDMA Read we can increment the msn now. See C9-148. */
101162306a36Sopenharmony_ci		qp->resp.msn++;
101262306a36Sopenharmony_ci		return RESPST_READ_REPLY;
101362306a36Sopenharmony_ci	} else if (pkt->mask & RXE_ATOMIC_MASK) {
101462306a36Sopenharmony_ci		return RESPST_ATOMIC_REPLY;
101562306a36Sopenharmony_ci	} else if (pkt->mask & RXE_ATOMIC_WRITE_MASK) {
101662306a36Sopenharmony_ci		return RESPST_ATOMIC_WRITE_REPLY;
101762306a36Sopenharmony_ci	} else if (pkt->mask & RXE_FLUSH_MASK) {
101862306a36Sopenharmony_ci		return RESPST_PROCESS_FLUSH;
101962306a36Sopenharmony_ci	} else {
102062306a36Sopenharmony_ci		/* Unreachable */
102162306a36Sopenharmony_ci		WARN_ON_ONCE(1);
102262306a36Sopenharmony_ci	}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	if (pkt->mask & RXE_IETH_MASK) {
102562306a36Sopenharmony_ci		u32 rkey = ieth_rkey(pkt);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci		err = invalidate_rkey(qp, rkey);
102862306a36Sopenharmony_ci		if (err)
102962306a36Sopenharmony_ci			return RESPST_ERR_INVALIDATE_RKEY;
103062306a36Sopenharmony_ci	}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	if (pkt->mask & RXE_END_MASK)
103362306a36Sopenharmony_ci		/* We successfully processed this new request. */
103462306a36Sopenharmony_ci		qp->resp.msn++;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	/* next expected psn, read handles this separately */
103762306a36Sopenharmony_ci	qp->resp.psn = (pkt->psn + 1) & BTH_PSN_MASK;
103862306a36Sopenharmony_ci	qp->resp.ack_psn = qp->resp.psn;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	qp->resp.opcode = pkt->opcode;
104162306a36Sopenharmony_ci	qp->resp.status = IB_WC_SUCCESS;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	if (pkt->mask & RXE_COMP_MASK)
104462306a36Sopenharmony_ci		return RESPST_COMPLETE;
104562306a36Sopenharmony_ci	else if (qp_type(qp) == IB_QPT_RC)
104662306a36Sopenharmony_ci		return RESPST_ACKNOWLEDGE;
104762306a36Sopenharmony_ci	else
104862306a36Sopenharmony_ci		return RESPST_CLEANUP;
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cistatic enum resp_states do_complete(struct rxe_qp *qp,
105262306a36Sopenharmony_ci				    struct rxe_pkt_info *pkt)
105362306a36Sopenharmony_ci{
105462306a36Sopenharmony_ci	struct rxe_cqe cqe;
105562306a36Sopenharmony_ci	struct ib_wc *wc = &cqe.ibwc;
105662306a36Sopenharmony_ci	struct ib_uverbs_wc *uwc = &cqe.uibwc;
105762306a36Sopenharmony_ci	struct rxe_recv_wqe *wqe = qp->resp.wqe;
105862306a36Sopenharmony_ci	struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
105962306a36Sopenharmony_ci	unsigned long flags;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	if (!wqe)
106262306a36Sopenharmony_ci		goto finish;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	memset(&cqe, 0, sizeof(cqe));
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	if (qp->rcq->is_user) {
106762306a36Sopenharmony_ci		uwc->status		= qp->resp.status;
106862306a36Sopenharmony_ci		uwc->qp_num		= qp->ibqp.qp_num;
106962306a36Sopenharmony_ci		uwc->wr_id		= wqe->wr_id;
107062306a36Sopenharmony_ci	} else {
107162306a36Sopenharmony_ci		wc->status		= qp->resp.status;
107262306a36Sopenharmony_ci		wc->qp			= &qp->ibqp;
107362306a36Sopenharmony_ci		wc->wr_id		= wqe->wr_id;
107462306a36Sopenharmony_ci	}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (wc->status == IB_WC_SUCCESS) {
107762306a36Sopenharmony_ci		rxe_counter_inc(rxe, RXE_CNT_RDMA_RECV);
107862306a36Sopenharmony_ci		wc->opcode = (pkt->mask & RXE_IMMDT_MASK &&
107962306a36Sopenharmony_ci				pkt->mask & RXE_WRITE_MASK) ?
108062306a36Sopenharmony_ci					IB_WC_RECV_RDMA_WITH_IMM : IB_WC_RECV;
108162306a36Sopenharmony_ci		wc->byte_len = (pkt->mask & RXE_IMMDT_MASK &&
108262306a36Sopenharmony_ci				pkt->mask & RXE_WRITE_MASK) ?
108362306a36Sopenharmony_ci					qp->resp.length : wqe->dma.length - wqe->dma.resid;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci		/* fields after byte_len are different between kernel and user
108662306a36Sopenharmony_ci		 * space
108762306a36Sopenharmony_ci		 */
108862306a36Sopenharmony_ci		if (qp->rcq->is_user) {
108962306a36Sopenharmony_ci			uwc->wc_flags = IB_WC_GRH;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci			if (pkt->mask & RXE_IMMDT_MASK) {
109262306a36Sopenharmony_ci				uwc->wc_flags |= IB_WC_WITH_IMM;
109362306a36Sopenharmony_ci				uwc->ex.imm_data = immdt_imm(pkt);
109462306a36Sopenharmony_ci			}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci			if (pkt->mask & RXE_IETH_MASK) {
109762306a36Sopenharmony_ci				uwc->wc_flags |= IB_WC_WITH_INVALIDATE;
109862306a36Sopenharmony_ci				uwc->ex.invalidate_rkey = ieth_rkey(pkt);
109962306a36Sopenharmony_ci			}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci			if (pkt->mask & RXE_DETH_MASK)
110262306a36Sopenharmony_ci				uwc->src_qp = deth_sqp(pkt);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci			uwc->port_num		= qp->attr.port_num;
110562306a36Sopenharmony_ci		} else {
110662306a36Sopenharmony_ci			struct sk_buff *skb = PKT_TO_SKB(pkt);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci			wc->wc_flags = IB_WC_GRH | IB_WC_WITH_NETWORK_HDR_TYPE;
110962306a36Sopenharmony_ci			if (skb->protocol == htons(ETH_P_IP))
111062306a36Sopenharmony_ci				wc->network_hdr_type = RDMA_NETWORK_IPV4;
111162306a36Sopenharmony_ci			else
111262306a36Sopenharmony_ci				wc->network_hdr_type = RDMA_NETWORK_IPV6;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci			if (is_vlan_dev(skb->dev)) {
111562306a36Sopenharmony_ci				wc->wc_flags |= IB_WC_WITH_VLAN;
111662306a36Sopenharmony_ci				wc->vlan_id = vlan_dev_vlan_id(skb->dev);
111762306a36Sopenharmony_ci			}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci			if (pkt->mask & RXE_IMMDT_MASK) {
112062306a36Sopenharmony_ci				wc->wc_flags |= IB_WC_WITH_IMM;
112162306a36Sopenharmony_ci				wc->ex.imm_data = immdt_imm(pkt);
112262306a36Sopenharmony_ci			}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci			if (pkt->mask & RXE_IETH_MASK) {
112562306a36Sopenharmony_ci				wc->wc_flags |= IB_WC_WITH_INVALIDATE;
112662306a36Sopenharmony_ci				wc->ex.invalidate_rkey = ieth_rkey(pkt);
112762306a36Sopenharmony_ci			}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci			if (pkt->mask & RXE_DETH_MASK)
113062306a36Sopenharmony_ci				wc->src_qp = deth_sqp(pkt);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci			wc->port_num		= qp->attr.port_num;
113362306a36Sopenharmony_ci		}
113462306a36Sopenharmony_ci	} else {
113562306a36Sopenharmony_ci		if (wc->status != IB_WC_WR_FLUSH_ERR)
113662306a36Sopenharmony_ci			rxe_err_qp(qp, "non-flush error status = %d",
113762306a36Sopenharmony_ci				wc->status);
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	/* have copy for srq and reference for !srq */
114162306a36Sopenharmony_ci	if (!qp->srq)
114262306a36Sopenharmony_ci		queue_advance_consumer(qp->rq.queue, QUEUE_TYPE_FROM_CLIENT);
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	qp->resp.wqe = NULL;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	if (rxe_cq_post(qp->rcq, &cqe, pkt ? bth_se(pkt) : 1))
114762306a36Sopenharmony_ci		return RESPST_ERR_CQ_OVERFLOW;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cifinish:
115062306a36Sopenharmony_ci	spin_lock_irqsave(&qp->state_lock, flags);
115162306a36Sopenharmony_ci	if (unlikely(qp_state(qp) == IB_QPS_ERR)) {
115262306a36Sopenharmony_ci		spin_unlock_irqrestore(&qp->state_lock, flags);
115362306a36Sopenharmony_ci		return RESPST_CHK_RESOURCE;
115462306a36Sopenharmony_ci	}
115562306a36Sopenharmony_ci	spin_unlock_irqrestore(&qp->state_lock, flags);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (unlikely(!pkt))
115862306a36Sopenharmony_ci		return RESPST_DONE;
115962306a36Sopenharmony_ci	if (qp_type(qp) == IB_QPT_RC)
116062306a36Sopenharmony_ci		return RESPST_ACKNOWLEDGE;
116162306a36Sopenharmony_ci	else
116262306a36Sopenharmony_ci		return RESPST_CLEANUP;
116362306a36Sopenharmony_ci}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_cistatic int send_common_ack(struct rxe_qp *qp, u8 syndrome, u32 psn,
116762306a36Sopenharmony_ci				  int opcode, const char *msg)
116862306a36Sopenharmony_ci{
116962306a36Sopenharmony_ci	int err;
117062306a36Sopenharmony_ci	struct rxe_pkt_info ack_pkt;
117162306a36Sopenharmony_ci	struct sk_buff *skb;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	skb = prepare_ack_packet(qp, &ack_pkt, opcode, 0, psn, syndrome);
117462306a36Sopenharmony_ci	if (!skb)
117562306a36Sopenharmony_ci		return -ENOMEM;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	err = rxe_xmit_packet(qp, &ack_pkt, skb);
117862306a36Sopenharmony_ci	if (err)
117962306a36Sopenharmony_ci		rxe_dbg_qp(qp, "Failed sending %s\n", msg);
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	return err;
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cistatic int send_ack(struct rxe_qp *qp, u8 syndrome, u32 psn)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	return send_common_ack(qp, syndrome, psn,
118762306a36Sopenharmony_ci			IB_OPCODE_RC_ACKNOWLEDGE, "ACK");
118862306a36Sopenharmony_ci}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_cistatic int send_atomic_ack(struct rxe_qp *qp, u8 syndrome, u32 psn)
119162306a36Sopenharmony_ci{
119262306a36Sopenharmony_ci	int ret = send_common_ack(qp, syndrome, psn,
119362306a36Sopenharmony_ci			IB_OPCODE_RC_ATOMIC_ACKNOWLEDGE, "ATOMIC ACK");
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	/* have to clear this since it is used to trigger
119662306a36Sopenharmony_ci	 * long read replies
119762306a36Sopenharmony_ci	 */
119862306a36Sopenharmony_ci	qp->resp.res = NULL;
119962306a36Sopenharmony_ci	return ret;
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_cistatic int send_read_response_ack(struct rxe_qp *qp, u8 syndrome, u32 psn)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	int ret = send_common_ack(qp, syndrome, psn,
120562306a36Sopenharmony_ci			IB_OPCODE_RC_RDMA_READ_RESPONSE_ONLY,
120662306a36Sopenharmony_ci			"RDMA READ response of length zero ACK");
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	/* have to clear this since it is used to trigger
120962306a36Sopenharmony_ci	 * long read replies
121062306a36Sopenharmony_ci	 */
121162306a36Sopenharmony_ci	qp->resp.res = NULL;
121262306a36Sopenharmony_ci	return ret;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_cistatic enum resp_states acknowledge(struct rxe_qp *qp,
121662306a36Sopenharmony_ci				    struct rxe_pkt_info *pkt)
121762306a36Sopenharmony_ci{
121862306a36Sopenharmony_ci	if (qp_type(qp) != IB_QPT_RC)
121962306a36Sopenharmony_ci		return RESPST_CLEANUP;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	if (qp->resp.aeth_syndrome != AETH_ACK_UNLIMITED)
122262306a36Sopenharmony_ci		send_ack(qp, qp->resp.aeth_syndrome, pkt->psn);
122362306a36Sopenharmony_ci	else if (pkt->mask & RXE_ATOMIC_MASK)
122462306a36Sopenharmony_ci		send_atomic_ack(qp, AETH_ACK_UNLIMITED, pkt->psn);
122562306a36Sopenharmony_ci	else if (pkt->mask & (RXE_FLUSH_MASK | RXE_ATOMIC_WRITE_MASK))
122662306a36Sopenharmony_ci		send_read_response_ack(qp, AETH_ACK_UNLIMITED, pkt->psn);
122762306a36Sopenharmony_ci	else if (bth_ack(pkt))
122862306a36Sopenharmony_ci		send_ack(qp, AETH_ACK_UNLIMITED, pkt->psn);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	return RESPST_CLEANUP;
123162306a36Sopenharmony_ci}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_cistatic enum resp_states cleanup(struct rxe_qp *qp,
123462306a36Sopenharmony_ci				struct rxe_pkt_info *pkt)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	struct sk_buff *skb;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	if (pkt) {
123962306a36Sopenharmony_ci		skb = skb_dequeue(&qp->req_pkts);
124062306a36Sopenharmony_ci		rxe_put(qp);
124162306a36Sopenharmony_ci		kfree_skb(skb);
124262306a36Sopenharmony_ci		ib_device_put(qp->ibqp.device);
124362306a36Sopenharmony_ci	}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	if (qp->resp.mr) {
124662306a36Sopenharmony_ci		rxe_put(qp->resp.mr);
124762306a36Sopenharmony_ci		qp->resp.mr = NULL;
124862306a36Sopenharmony_ci	}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	return RESPST_DONE;
125162306a36Sopenharmony_ci}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_cistatic struct resp_res *find_resource(struct rxe_qp *qp, u32 psn)
125462306a36Sopenharmony_ci{
125562306a36Sopenharmony_ci	int i;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	for (i = 0; i < qp->attr.max_dest_rd_atomic; i++) {
125862306a36Sopenharmony_ci		struct resp_res *res = &qp->resp.resources[i];
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci		if (res->type == 0)
126162306a36Sopenharmony_ci			continue;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci		if (psn_compare(psn, res->first_psn) >= 0 &&
126462306a36Sopenharmony_ci		    psn_compare(psn, res->last_psn) <= 0) {
126562306a36Sopenharmony_ci			return res;
126662306a36Sopenharmony_ci		}
126762306a36Sopenharmony_ci	}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	return NULL;
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_cistatic enum resp_states duplicate_request(struct rxe_qp *qp,
127362306a36Sopenharmony_ci					  struct rxe_pkt_info *pkt)
127462306a36Sopenharmony_ci{
127562306a36Sopenharmony_ci	enum resp_states rc;
127662306a36Sopenharmony_ci	u32 prev_psn = (qp->resp.ack_psn - 1) & BTH_PSN_MASK;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (pkt->mask & RXE_SEND_MASK ||
127962306a36Sopenharmony_ci	    pkt->mask & RXE_WRITE_MASK) {
128062306a36Sopenharmony_ci		/* SEND. Ack again and cleanup. C9-105. */
128162306a36Sopenharmony_ci		send_ack(qp, AETH_ACK_UNLIMITED, prev_psn);
128262306a36Sopenharmony_ci		return RESPST_CLEANUP;
128362306a36Sopenharmony_ci	} else if (pkt->mask & RXE_FLUSH_MASK) {
128462306a36Sopenharmony_ci		struct resp_res *res;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci		/* Find the operation in our list of responder resources. */
128762306a36Sopenharmony_ci		res = find_resource(qp, pkt->psn);
128862306a36Sopenharmony_ci		if (res) {
128962306a36Sopenharmony_ci			res->replay = 1;
129062306a36Sopenharmony_ci			res->cur_psn = pkt->psn;
129162306a36Sopenharmony_ci			qp->resp.res = res;
129262306a36Sopenharmony_ci			rc = RESPST_PROCESS_FLUSH;
129362306a36Sopenharmony_ci			goto out;
129462306a36Sopenharmony_ci		}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci		/* Resource not found. Class D error. Drop the request. */
129762306a36Sopenharmony_ci		rc = RESPST_CLEANUP;
129862306a36Sopenharmony_ci		goto out;
129962306a36Sopenharmony_ci	} else if (pkt->mask & RXE_READ_MASK) {
130062306a36Sopenharmony_ci		struct resp_res *res;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci		res = find_resource(qp, pkt->psn);
130362306a36Sopenharmony_ci		if (!res) {
130462306a36Sopenharmony_ci			/* Resource not found. Class D error.  Drop the
130562306a36Sopenharmony_ci			 * request.
130662306a36Sopenharmony_ci			 */
130762306a36Sopenharmony_ci			rc = RESPST_CLEANUP;
130862306a36Sopenharmony_ci			goto out;
130962306a36Sopenharmony_ci		} else {
131062306a36Sopenharmony_ci			/* Ensure this new request is the same as the previous
131162306a36Sopenharmony_ci			 * one or a subset of it.
131262306a36Sopenharmony_ci			 */
131362306a36Sopenharmony_ci			u64 iova = reth_va(pkt);
131462306a36Sopenharmony_ci			u32 resid = reth_len(pkt);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci			if (iova < res->read.va_org ||
131762306a36Sopenharmony_ci			    resid > res->read.length ||
131862306a36Sopenharmony_ci			    (iova + resid) > (res->read.va_org +
131962306a36Sopenharmony_ci					      res->read.length)) {
132062306a36Sopenharmony_ci				rc = RESPST_CLEANUP;
132162306a36Sopenharmony_ci				goto out;
132262306a36Sopenharmony_ci			}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci			if (reth_rkey(pkt) != res->read.rkey) {
132562306a36Sopenharmony_ci				rc = RESPST_CLEANUP;
132662306a36Sopenharmony_ci				goto out;
132762306a36Sopenharmony_ci			}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci			res->cur_psn = pkt->psn;
133062306a36Sopenharmony_ci			res->state = (pkt->psn == res->first_psn) ?
133162306a36Sopenharmony_ci					rdatm_res_state_new :
133262306a36Sopenharmony_ci					rdatm_res_state_replay;
133362306a36Sopenharmony_ci			res->replay = 1;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci			/* Reset the resource, except length. */
133662306a36Sopenharmony_ci			res->read.va_org = iova;
133762306a36Sopenharmony_ci			res->read.va = iova;
133862306a36Sopenharmony_ci			res->read.resid = resid;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci			/* Replay the RDMA read reply. */
134162306a36Sopenharmony_ci			qp->resp.res = res;
134262306a36Sopenharmony_ci			rc = RESPST_READ_REPLY;
134362306a36Sopenharmony_ci			goto out;
134462306a36Sopenharmony_ci		}
134562306a36Sopenharmony_ci	} else {
134662306a36Sopenharmony_ci		struct resp_res *res;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci		/* Find the operation in our list of responder resources. */
134962306a36Sopenharmony_ci		res = find_resource(qp, pkt->psn);
135062306a36Sopenharmony_ci		if (res) {
135162306a36Sopenharmony_ci			res->replay = 1;
135262306a36Sopenharmony_ci			res->cur_psn = pkt->psn;
135362306a36Sopenharmony_ci			qp->resp.res = res;
135462306a36Sopenharmony_ci			rc = pkt->mask & RXE_ATOMIC_MASK ?
135562306a36Sopenharmony_ci					RESPST_ATOMIC_REPLY :
135662306a36Sopenharmony_ci					RESPST_ATOMIC_WRITE_REPLY;
135762306a36Sopenharmony_ci			goto out;
135862306a36Sopenharmony_ci		}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci		/* Resource not found. Class D error. Drop the request. */
136162306a36Sopenharmony_ci		rc = RESPST_CLEANUP;
136262306a36Sopenharmony_ci		goto out;
136362306a36Sopenharmony_ci	}
136462306a36Sopenharmony_ciout:
136562306a36Sopenharmony_ci	return rc;
136662306a36Sopenharmony_ci}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci/* Process a class A or C. Both are treated the same in this implementation. */
136962306a36Sopenharmony_cistatic void do_class_ac_error(struct rxe_qp *qp, u8 syndrome,
137062306a36Sopenharmony_ci			      enum ib_wc_status status)
137162306a36Sopenharmony_ci{
137262306a36Sopenharmony_ci	qp->resp.aeth_syndrome	= syndrome;
137362306a36Sopenharmony_ci	qp->resp.status		= status;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	/* indicate that we should go through the ERROR state */
137662306a36Sopenharmony_ci	qp->resp.goto_error	= 1;
137762306a36Sopenharmony_ci}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_cistatic enum resp_states do_class_d1e_error(struct rxe_qp *qp)
138062306a36Sopenharmony_ci{
138162306a36Sopenharmony_ci	/* UC */
138262306a36Sopenharmony_ci	if (qp->srq) {
138362306a36Sopenharmony_ci		/* Class E */
138462306a36Sopenharmony_ci		qp->resp.drop_msg = 1;
138562306a36Sopenharmony_ci		if (qp->resp.wqe) {
138662306a36Sopenharmony_ci			qp->resp.status = IB_WC_REM_INV_REQ_ERR;
138762306a36Sopenharmony_ci			return RESPST_COMPLETE;
138862306a36Sopenharmony_ci		} else {
138962306a36Sopenharmony_ci			return RESPST_CLEANUP;
139062306a36Sopenharmony_ci		}
139162306a36Sopenharmony_ci	} else {
139262306a36Sopenharmony_ci		/* Class D1. This packet may be the start of a
139362306a36Sopenharmony_ci		 * new message and could be valid. The previous
139462306a36Sopenharmony_ci		 * message is invalid and ignored. reset the
139562306a36Sopenharmony_ci		 * recv wr to its original state
139662306a36Sopenharmony_ci		 */
139762306a36Sopenharmony_ci		if (qp->resp.wqe) {
139862306a36Sopenharmony_ci			qp->resp.wqe->dma.resid = qp->resp.wqe->dma.length;
139962306a36Sopenharmony_ci			qp->resp.wqe->dma.cur_sge = 0;
140062306a36Sopenharmony_ci			qp->resp.wqe->dma.sge_offset = 0;
140162306a36Sopenharmony_ci			qp->resp.opcode = -1;
140262306a36Sopenharmony_ci		}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci		if (qp->resp.mr) {
140562306a36Sopenharmony_ci			rxe_put(qp->resp.mr);
140662306a36Sopenharmony_ci			qp->resp.mr = NULL;
140762306a36Sopenharmony_ci		}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci		return RESPST_CLEANUP;
141062306a36Sopenharmony_ci	}
141162306a36Sopenharmony_ci}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci/* drain incoming request packet queue */
141462306a36Sopenharmony_cistatic void drain_req_pkts(struct rxe_qp *qp)
141562306a36Sopenharmony_ci{
141662306a36Sopenharmony_ci	struct sk_buff *skb;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	while ((skb = skb_dequeue(&qp->req_pkts))) {
141962306a36Sopenharmony_ci		rxe_put(qp);
142062306a36Sopenharmony_ci		kfree_skb(skb);
142162306a36Sopenharmony_ci		ib_device_put(qp->ibqp.device);
142262306a36Sopenharmony_ci	}
142362306a36Sopenharmony_ci}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci/* complete receive wqe with flush error */
142662306a36Sopenharmony_cistatic int flush_recv_wqe(struct rxe_qp *qp, struct rxe_recv_wqe *wqe)
142762306a36Sopenharmony_ci{
142862306a36Sopenharmony_ci	struct rxe_cqe cqe = {};
142962306a36Sopenharmony_ci	struct ib_wc *wc = &cqe.ibwc;
143062306a36Sopenharmony_ci	struct ib_uverbs_wc *uwc = &cqe.uibwc;
143162306a36Sopenharmony_ci	int err;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	if (qp->rcq->is_user) {
143462306a36Sopenharmony_ci		uwc->wr_id = wqe->wr_id;
143562306a36Sopenharmony_ci		uwc->status = IB_WC_WR_FLUSH_ERR;
143662306a36Sopenharmony_ci		uwc->qp_num = qp_num(qp);
143762306a36Sopenharmony_ci	} else {
143862306a36Sopenharmony_ci		wc->wr_id = wqe->wr_id;
143962306a36Sopenharmony_ci		wc->status = IB_WC_WR_FLUSH_ERR;
144062306a36Sopenharmony_ci		wc->qp = &qp->ibqp;
144162306a36Sopenharmony_ci	}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	err = rxe_cq_post(qp->rcq, &cqe, 0);
144462306a36Sopenharmony_ci	if (err)
144562306a36Sopenharmony_ci		rxe_dbg_cq(qp->rcq, "post cq failed err = %d", err);
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	return err;
144862306a36Sopenharmony_ci}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci/* drain and optionally complete the recive queue
145162306a36Sopenharmony_ci * if unable to complete a wqe stop completing and
145262306a36Sopenharmony_ci * just flush the remaining wqes
145362306a36Sopenharmony_ci */
145462306a36Sopenharmony_cistatic void flush_recv_queue(struct rxe_qp *qp, bool notify)
145562306a36Sopenharmony_ci{
145662306a36Sopenharmony_ci	struct rxe_queue *q = qp->rq.queue;
145762306a36Sopenharmony_ci	struct rxe_recv_wqe *wqe;
145862306a36Sopenharmony_ci	int err;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	if (qp->srq) {
146162306a36Sopenharmony_ci		if (notify && qp->ibqp.event_handler) {
146262306a36Sopenharmony_ci			struct ib_event ev;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci			ev.device = qp->ibqp.device;
146562306a36Sopenharmony_ci			ev.element.qp = &qp->ibqp;
146662306a36Sopenharmony_ci			ev.event = IB_EVENT_QP_LAST_WQE_REACHED;
146762306a36Sopenharmony_ci			qp->ibqp.event_handler(&ev, qp->ibqp.qp_context);
146862306a36Sopenharmony_ci		}
146962306a36Sopenharmony_ci		return;
147062306a36Sopenharmony_ci	}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	/* recv queue not created. nothing to do. */
147362306a36Sopenharmony_ci	if (!qp->rq.queue)
147462306a36Sopenharmony_ci		return;
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	while ((wqe = queue_head(q, q->type))) {
147762306a36Sopenharmony_ci		if (notify) {
147862306a36Sopenharmony_ci			err = flush_recv_wqe(qp, wqe);
147962306a36Sopenharmony_ci			if (err)
148062306a36Sopenharmony_ci				notify = 0;
148162306a36Sopenharmony_ci		}
148262306a36Sopenharmony_ci		queue_advance_consumer(q, q->type);
148362306a36Sopenharmony_ci	}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	qp->resp.wqe = NULL;
148662306a36Sopenharmony_ci}
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ciint rxe_responder(struct rxe_qp *qp)
148962306a36Sopenharmony_ci{
149062306a36Sopenharmony_ci	struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
149162306a36Sopenharmony_ci	enum resp_states state;
149262306a36Sopenharmony_ci	struct rxe_pkt_info *pkt = NULL;
149362306a36Sopenharmony_ci	int ret;
149462306a36Sopenharmony_ci	unsigned long flags;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	spin_lock_irqsave(&qp->state_lock, flags);
149762306a36Sopenharmony_ci	if (!qp->valid || qp_state(qp) == IB_QPS_ERR ||
149862306a36Sopenharmony_ci			  qp_state(qp) == IB_QPS_RESET) {
149962306a36Sopenharmony_ci		bool notify = qp->valid && (qp_state(qp) == IB_QPS_ERR);
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci		drain_req_pkts(qp);
150262306a36Sopenharmony_ci		flush_recv_queue(qp, notify);
150362306a36Sopenharmony_ci		spin_unlock_irqrestore(&qp->state_lock, flags);
150462306a36Sopenharmony_ci		goto exit;
150562306a36Sopenharmony_ci	}
150662306a36Sopenharmony_ci	spin_unlock_irqrestore(&qp->state_lock, flags);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	qp->resp.aeth_syndrome = AETH_ACK_UNLIMITED;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	state = RESPST_GET_REQ;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	while (1) {
151362306a36Sopenharmony_ci		rxe_dbg_qp(qp, "state = %s\n", resp_state_name[state]);
151462306a36Sopenharmony_ci		switch (state) {
151562306a36Sopenharmony_ci		case RESPST_GET_REQ:
151662306a36Sopenharmony_ci			state = get_req(qp, &pkt);
151762306a36Sopenharmony_ci			break;
151862306a36Sopenharmony_ci		case RESPST_CHK_PSN:
151962306a36Sopenharmony_ci			state = check_psn(qp, pkt);
152062306a36Sopenharmony_ci			break;
152162306a36Sopenharmony_ci		case RESPST_CHK_OP_SEQ:
152262306a36Sopenharmony_ci			state = check_op_seq(qp, pkt);
152362306a36Sopenharmony_ci			break;
152462306a36Sopenharmony_ci		case RESPST_CHK_OP_VALID:
152562306a36Sopenharmony_ci			state = check_op_valid(qp, pkt);
152662306a36Sopenharmony_ci			break;
152762306a36Sopenharmony_ci		case RESPST_CHK_RESOURCE:
152862306a36Sopenharmony_ci			state = check_resource(qp, pkt);
152962306a36Sopenharmony_ci			break;
153062306a36Sopenharmony_ci		case RESPST_CHK_LENGTH:
153162306a36Sopenharmony_ci			state = rxe_resp_check_length(qp, pkt);
153262306a36Sopenharmony_ci			break;
153362306a36Sopenharmony_ci		case RESPST_CHK_RKEY:
153462306a36Sopenharmony_ci			state = check_rkey(qp, pkt);
153562306a36Sopenharmony_ci			break;
153662306a36Sopenharmony_ci		case RESPST_EXECUTE:
153762306a36Sopenharmony_ci			state = execute(qp, pkt);
153862306a36Sopenharmony_ci			break;
153962306a36Sopenharmony_ci		case RESPST_COMPLETE:
154062306a36Sopenharmony_ci			state = do_complete(qp, pkt);
154162306a36Sopenharmony_ci			break;
154262306a36Sopenharmony_ci		case RESPST_READ_REPLY:
154362306a36Sopenharmony_ci			state = read_reply(qp, pkt);
154462306a36Sopenharmony_ci			break;
154562306a36Sopenharmony_ci		case RESPST_ATOMIC_REPLY:
154662306a36Sopenharmony_ci			state = atomic_reply(qp, pkt);
154762306a36Sopenharmony_ci			break;
154862306a36Sopenharmony_ci		case RESPST_ATOMIC_WRITE_REPLY:
154962306a36Sopenharmony_ci			state = atomic_write_reply(qp, pkt);
155062306a36Sopenharmony_ci			break;
155162306a36Sopenharmony_ci		case RESPST_PROCESS_FLUSH:
155262306a36Sopenharmony_ci			state = process_flush(qp, pkt);
155362306a36Sopenharmony_ci			break;
155462306a36Sopenharmony_ci		case RESPST_ACKNOWLEDGE:
155562306a36Sopenharmony_ci			state = acknowledge(qp, pkt);
155662306a36Sopenharmony_ci			break;
155762306a36Sopenharmony_ci		case RESPST_CLEANUP:
155862306a36Sopenharmony_ci			state = cleanup(qp, pkt);
155962306a36Sopenharmony_ci			break;
156062306a36Sopenharmony_ci		case RESPST_DUPLICATE_REQUEST:
156162306a36Sopenharmony_ci			state = duplicate_request(qp, pkt);
156262306a36Sopenharmony_ci			break;
156362306a36Sopenharmony_ci		case RESPST_ERR_PSN_OUT_OF_SEQ:
156462306a36Sopenharmony_ci			/* RC only - Class B. Drop packet. */
156562306a36Sopenharmony_ci			send_ack(qp, AETH_NAK_PSN_SEQ_ERROR, qp->resp.psn);
156662306a36Sopenharmony_ci			state = RESPST_CLEANUP;
156762306a36Sopenharmony_ci			break;
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci		case RESPST_ERR_TOO_MANY_RDMA_ATM_REQ:
157062306a36Sopenharmony_ci		case RESPST_ERR_MISSING_OPCODE_FIRST:
157162306a36Sopenharmony_ci		case RESPST_ERR_MISSING_OPCODE_LAST_C:
157262306a36Sopenharmony_ci		case RESPST_ERR_UNSUPPORTED_OPCODE:
157362306a36Sopenharmony_ci		case RESPST_ERR_MISALIGNED_ATOMIC:
157462306a36Sopenharmony_ci			/* RC Only - Class C. */
157562306a36Sopenharmony_ci			do_class_ac_error(qp, AETH_NAK_INVALID_REQ,
157662306a36Sopenharmony_ci					  IB_WC_REM_INV_REQ_ERR);
157762306a36Sopenharmony_ci			state = RESPST_COMPLETE;
157862306a36Sopenharmony_ci			break;
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci		case RESPST_ERR_MISSING_OPCODE_LAST_D1E:
158162306a36Sopenharmony_ci			state = do_class_d1e_error(qp);
158262306a36Sopenharmony_ci			break;
158362306a36Sopenharmony_ci		case RESPST_ERR_RNR:
158462306a36Sopenharmony_ci			if (qp_type(qp) == IB_QPT_RC) {
158562306a36Sopenharmony_ci				rxe_counter_inc(rxe, RXE_CNT_SND_RNR);
158662306a36Sopenharmony_ci				/* RC - class B */
158762306a36Sopenharmony_ci				send_ack(qp, AETH_RNR_NAK |
158862306a36Sopenharmony_ci					 (~AETH_TYPE_MASK &
158962306a36Sopenharmony_ci					 qp->attr.min_rnr_timer),
159062306a36Sopenharmony_ci					 pkt->psn);
159162306a36Sopenharmony_ci			} else {
159262306a36Sopenharmony_ci				/* UD/UC - class D */
159362306a36Sopenharmony_ci				qp->resp.drop_msg = 1;
159462306a36Sopenharmony_ci			}
159562306a36Sopenharmony_ci			state = RESPST_CLEANUP;
159662306a36Sopenharmony_ci			break;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci		case RESPST_ERR_RKEY_VIOLATION:
159962306a36Sopenharmony_ci			if (qp_type(qp) == IB_QPT_RC) {
160062306a36Sopenharmony_ci				/* Class C */
160162306a36Sopenharmony_ci				do_class_ac_error(qp, AETH_NAK_REM_ACC_ERR,
160262306a36Sopenharmony_ci						  IB_WC_REM_ACCESS_ERR);
160362306a36Sopenharmony_ci				state = RESPST_COMPLETE;
160462306a36Sopenharmony_ci			} else {
160562306a36Sopenharmony_ci				qp->resp.drop_msg = 1;
160662306a36Sopenharmony_ci				if (qp->srq) {
160762306a36Sopenharmony_ci					/* UC/SRQ Class D */
160862306a36Sopenharmony_ci					qp->resp.status = IB_WC_REM_ACCESS_ERR;
160962306a36Sopenharmony_ci					state = RESPST_COMPLETE;
161062306a36Sopenharmony_ci				} else {
161162306a36Sopenharmony_ci					/* UC/non-SRQ Class E. */
161262306a36Sopenharmony_ci					state = RESPST_CLEANUP;
161362306a36Sopenharmony_ci				}
161462306a36Sopenharmony_ci			}
161562306a36Sopenharmony_ci			break;
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci		case RESPST_ERR_INVALIDATE_RKEY:
161862306a36Sopenharmony_ci			/* RC - Class J. */
161962306a36Sopenharmony_ci			qp->resp.goto_error = 1;
162062306a36Sopenharmony_ci			qp->resp.status = IB_WC_REM_INV_REQ_ERR;
162162306a36Sopenharmony_ci			state = RESPST_COMPLETE;
162262306a36Sopenharmony_ci			break;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci		case RESPST_ERR_LENGTH:
162562306a36Sopenharmony_ci			if (qp_type(qp) == IB_QPT_RC) {
162662306a36Sopenharmony_ci				/* Class C */
162762306a36Sopenharmony_ci				do_class_ac_error(qp, AETH_NAK_INVALID_REQ,
162862306a36Sopenharmony_ci						  IB_WC_REM_INV_REQ_ERR);
162962306a36Sopenharmony_ci				state = RESPST_COMPLETE;
163062306a36Sopenharmony_ci			} else if (qp->srq) {
163162306a36Sopenharmony_ci				/* UC/UD - class E */
163262306a36Sopenharmony_ci				qp->resp.status = IB_WC_REM_INV_REQ_ERR;
163362306a36Sopenharmony_ci				state = RESPST_COMPLETE;
163462306a36Sopenharmony_ci			} else {
163562306a36Sopenharmony_ci				/* UC/UD - class D */
163662306a36Sopenharmony_ci				qp->resp.drop_msg = 1;
163762306a36Sopenharmony_ci				state = RESPST_CLEANUP;
163862306a36Sopenharmony_ci			}
163962306a36Sopenharmony_ci			break;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci		case RESPST_ERR_MALFORMED_WQE:
164262306a36Sopenharmony_ci			/* All, Class A. */
164362306a36Sopenharmony_ci			do_class_ac_error(qp, AETH_NAK_REM_OP_ERR,
164462306a36Sopenharmony_ci					  IB_WC_LOC_QP_OP_ERR);
164562306a36Sopenharmony_ci			state = RESPST_COMPLETE;
164662306a36Sopenharmony_ci			break;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci		case RESPST_ERR_CQ_OVERFLOW:
164962306a36Sopenharmony_ci			/* All - Class G */
165062306a36Sopenharmony_ci			state = RESPST_ERROR;
165162306a36Sopenharmony_ci			break;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci		case RESPST_DONE:
165462306a36Sopenharmony_ci			if (qp->resp.goto_error) {
165562306a36Sopenharmony_ci				state = RESPST_ERROR;
165662306a36Sopenharmony_ci				break;
165762306a36Sopenharmony_ci			}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci			goto done;
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci		case RESPST_EXIT:
166262306a36Sopenharmony_ci			if (qp->resp.goto_error) {
166362306a36Sopenharmony_ci				state = RESPST_ERROR;
166462306a36Sopenharmony_ci				break;
166562306a36Sopenharmony_ci			}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci			goto exit;
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci		case RESPST_ERROR:
167062306a36Sopenharmony_ci			qp->resp.goto_error = 0;
167162306a36Sopenharmony_ci			rxe_dbg_qp(qp, "moved to error state\n");
167262306a36Sopenharmony_ci			rxe_qp_error(qp);
167362306a36Sopenharmony_ci			goto exit;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci		default:
167662306a36Sopenharmony_ci			WARN_ON_ONCE(1);
167762306a36Sopenharmony_ci		}
167862306a36Sopenharmony_ci	}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	/* A non-zero return value will cause rxe_do_task to
168162306a36Sopenharmony_ci	 * exit its loop and end the work item. A zero return
168262306a36Sopenharmony_ci	 * will continue looping and return to rxe_responder
168362306a36Sopenharmony_ci	 */
168462306a36Sopenharmony_cidone:
168562306a36Sopenharmony_ci	ret = 0;
168662306a36Sopenharmony_ci	goto out;
168762306a36Sopenharmony_ciexit:
168862306a36Sopenharmony_ci	ret = -EAGAIN;
168962306a36Sopenharmony_ciout:
169062306a36Sopenharmony_ci	return ret;
169162306a36Sopenharmony_ci}
1692