xref: /onnv-gate/usr/src/uts/common/io/ib/adapters/hermon/hermon_wr.c (revision 12965:b65a8427f8fe)
19517SBill.Taylor@Sun.COM /*
29517SBill.Taylor@Sun.COM  * CDDL HEADER START
39517SBill.Taylor@Sun.COM  *
49517SBill.Taylor@Sun.COM  * The contents of this file are subject to the terms of the
59517SBill.Taylor@Sun.COM  * Common Development and Distribution License (the "License").
69517SBill.Taylor@Sun.COM  * You may not use this file except in compliance with the License.
79517SBill.Taylor@Sun.COM  *
89517SBill.Taylor@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99517SBill.Taylor@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109517SBill.Taylor@Sun.COM  * See the License for the specific language governing permissions
119517SBill.Taylor@Sun.COM  * and limitations under the License.
129517SBill.Taylor@Sun.COM  *
139517SBill.Taylor@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149517SBill.Taylor@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159517SBill.Taylor@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169517SBill.Taylor@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179517SBill.Taylor@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189517SBill.Taylor@Sun.COM  *
199517SBill.Taylor@Sun.COM  * CDDL HEADER END
209517SBill.Taylor@Sun.COM  */
219517SBill.Taylor@Sun.COM 
229517SBill.Taylor@Sun.COM /*
23*12965SWilliam.Taylor@Oracle.COM  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
249517SBill.Taylor@Sun.COM  */
259517SBill.Taylor@Sun.COM 
269517SBill.Taylor@Sun.COM /*
279517SBill.Taylor@Sun.COM  * hermon_wr.c
289517SBill.Taylor@Sun.COM  *    Hermon Work Request Processing Routines
299517SBill.Taylor@Sun.COM  *
309517SBill.Taylor@Sun.COM  *    Implements all the routines necessary to provide the PostSend(),
319517SBill.Taylor@Sun.COM  *    PostRecv() and PostSRQ() verbs.  Also contains all the code
329517SBill.Taylor@Sun.COM  *    necessary to implement the Hermon WRID tracking mechanism.
339517SBill.Taylor@Sun.COM  */
349517SBill.Taylor@Sun.COM 
359517SBill.Taylor@Sun.COM #include <sys/types.h>
369517SBill.Taylor@Sun.COM #include <sys/conf.h>
379517SBill.Taylor@Sun.COM #include <sys/ddi.h>
389517SBill.Taylor@Sun.COM #include <sys/sunddi.h>
399517SBill.Taylor@Sun.COM #include <sys/modctl.h>
409517SBill.Taylor@Sun.COM #include <sys/avl.h>
419517SBill.Taylor@Sun.COM 
429517SBill.Taylor@Sun.COM #include <sys/ib/adapters/hermon/hermon.h>
439517SBill.Taylor@Sun.COM 
449517SBill.Taylor@Sun.COM static uint32_t hermon_wr_get_immediate(ibt_send_wr_t *wr);
459517SBill.Taylor@Sun.COM static int hermon_wr_bind_check(hermon_state_t *state, ibt_send_wr_t *wr);
469517SBill.Taylor@Sun.COM static int hermon_wqe_send_build(hermon_state_t *state, hermon_qphdl_t qp,
479517SBill.Taylor@Sun.COM     ibt_send_wr_t *wr, uint64_t *desc, uint_t *size);
489517SBill.Taylor@Sun.COM static int hermon_wqe_mlx_build(hermon_state_t *state, hermon_qphdl_t qp,
499517SBill.Taylor@Sun.COM     ibt_send_wr_t *wr, uint64_t *desc, uint_t *size);
509517SBill.Taylor@Sun.COM static void hermon_wqe_headroom(uint_t from, hermon_qphdl_t qp);
519517SBill.Taylor@Sun.COM static int hermon_wqe_recv_build(hermon_state_t *state, hermon_qphdl_t qp,
529517SBill.Taylor@Sun.COM     ibt_recv_wr_t *wr, uint64_t *desc);
539517SBill.Taylor@Sun.COM static int hermon_wqe_srq_build(hermon_state_t *state, hermon_srqhdl_t srq,
549517SBill.Taylor@Sun.COM     ibt_recv_wr_t *wr, uint64_t *desc);
559517SBill.Taylor@Sun.COM static hermon_workq_avl_t *hermon_wrid_wqavl_find(hermon_cqhdl_t cq, uint_t qpn,
569517SBill.Taylor@Sun.COM     uint_t send_or_recv);
579517SBill.Taylor@Sun.COM static void hermon_cq_workq_add(hermon_cqhdl_t cq, hermon_workq_avl_t *wqavl);
589517SBill.Taylor@Sun.COM static void hermon_cq_workq_remove(hermon_cqhdl_t cq,
599517SBill.Taylor@Sun.COM     hermon_workq_avl_t *wqavl);
609517SBill.Taylor@Sun.COM 
619517SBill.Taylor@Sun.COM static	ibt_wr_ds_t	null_sgl = { 0, 0x00000100, 0 };
629517SBill.Taylor@Sun.COM 
6311972SBill.Taylor@Sun.COM /*
6411972SBill.Taylor@Sun.COM  * Add ability to try to debug RDMA_READ/RDMA_WRITE failures.
6511972SBill.Taylor@Sun.COM  *
6611972SBill.Taylor@Sun.COM  *      0x1 - print rkey used during post_send
6711972SBill.Taylor@Sun.COM  *      0x2 - print sgls used during post_send
6811972SBill.Taylor@Sun.COM  *	0x4 - print FMR comings and goings
6911972SBill.Taylor@Sun.COM  */
7011972SBill.Taylor@Sun.COM int hermon_rdma_debug = 0x0;
7111972SBill.Taylor@Sun.COM 
729517SBill.Taylor@Sun.COM static int
hermon_post_send_ud(hermon_state_t * state,hermon_qphdl_t qp,ibt_send_wr_t * wr,uint_t num_wr,uint_t * num_posted)739517SBill.Taylor@Sun.COM hermon_post_send_ud(hermon_state_t *state, hermon_qphdl_t qp,
749517SBill.Taylor@Sun.COM     ibt_send_wr_t *wr, uint_t num_wr, uint_t *num_posted)
759517SBill.Taylor@Sun.COM {
769517SBill.Taylor@Sun.COM 	hermon_hw_snd_wqe_ud_t		*ud;
779517SBill.Taylor@Sun.COM 	hermon_workq_hdr_t		*wq;
789517SBill.Taylor@Sun.COM 	hermon_ahhdl_t			ah;
79*12965SWilliam.Taylor@Oracle.COM 	ibt_wr_rfci_send_t		*rfci;
80*12965SWilliam.Taylor@Oracle.COM 	ibt_wr_init_send_t		*is;
819517SBill.Taylor@Sun.COM 	ibt_ud_dest_t			*dest;
829517SBill.Taylor@Sun.COM 	uint64_t			*desc;
839517SBill.Taylor@Sun.COM 	uint32_t			desc_sz;
849517SBill.Taylor@Sun.COM 	uint32_t			signaled_dbd, solicited;
859517SBill.Taylor@Sun.COM 	uint32_t			head, tail, next_tail, qsize_msk;
869517SBill.Taylor@Sun.COM 	uint32_t			hdrmwqes;
879517SBill.Taylor@Sun.COM 	uint32_t			nopcode, fence, immed_data = 0;
8810633SRajkumar.Sivaprakasam@Sun.COM 	hermon_hw_wqe_sgl_t		*ds, *old_ds;
899517SBill.Taylor@Sun.COM 	ibt_wr_ds_t			*sgl;
90*12965SWilliam.Taylor@Oracle.COM 	int				nds;
919517SBill.Taylor@Sun.COM 	int				i, j, last_ds, num_ds, status;
929517SBill.Taylor@Sun.COM 	uint32_t			*wqe_start;
939517SBill.Taylor@Sun.COM 	int				sectperwqe;
949517SBill.Taylor@Sun.COM 	uint_t				posted_cnt = 0;
95*12965SWilliam.Taylor@Oracle.COM 	int				total_len, strong_order, fc_bits, cksum;
96*12965SWilliam.Taylor@Oracle.COM 
979517SBill.Taylor@Sun.COM 
989517SBill.Taylor@Sun.COM 	/* initialize the FMA retry loop */
999517SBill.Taylor@Sun.COM 	hermon_pio_init(fm_loop_cnt, fm_status, fm_test_num);
1009517SBill.Taylor@Sun.COM 
1019517SBill.Taylor@Sun.COM 	ASSERT(MUTEX_HELD(&qp->qp_sq_lock));
1029517SBill.Taylor@Sun.COM 	_NOTE(LOCK_RELEASED_AS_SIDE_EFFECT(&qp->qp_sq_lock))
1039517SBill.Taylor@Sun.COM 
1049517SBill.Taylor@Sun.COM 	/* Grab the lock for the WRID list */
1059517SBill.Taylor@Sun.COM 	membar_consumer();
1069517SBill.Taylor@Sun.COM 
1079517SBill.Taylor@Sun.COM 	/* Save away some initial QP state */
1089517SBill.Taylor@Sun.COM 	wq = qp->qp_sq_wqhdr;
1099517SBill.Taylor@Sun.COM 	qsize_msk = wq->wq_mask;
1109517SBill.Taylor@Sun.COM 	hdrmwqes  = qp->qp_sq_hdrmwqes;		/* in WQEs  */
1119517SBill.Taylor@Sun.COM 	sectperwqe = 1 << (qp->qp_sq_log_wqesz - 2);
1129517SBill.Taylor@Sun.COM 
1139517SBill.Taylor@Sun.COM 	tail	  = wq->wq_tail;
1149517SBill.Taylor@Sun.COM 	head	  = wq->wq_head;
1159517SBill.Taylor@Sun.COM 	status	  = DDI_SUCCESS;
1169517SBill.Taylor@Sun.COM 
1179517SBill.Taylor@Sun.COM post_next:
1189517SBill.Taylor@Sun.COM 	/*
1199517SBill.Taylor@Sun.COM 	 * Check for "queue full" condition.  If the queue
1209517SBill.Taylor@Sun.COM 	 * is already full, then no more WQEs can be posted.
1219517SBill.Taylor@Sun.COM 	 * So break out, ring a doorbell (if necessary) and
1229517SBill.Taylor@Sun.COM 	 * return an error
1239517SBill.Taylor@Sun.COM 	 */
1249517SBill.Taylor@Sun.COM 	if (wq->wq_full != 0) {
1259517SBill.Taylor@Sun.COM 		status = IBT_QP_FULL;
1269517SBill.Taylor@Sun.COM 		goto done;
1279517SBill.Taylor@Sun.COM 	}
1289517SBill.Taylor@Sun.COM 
1299517SBill.Taylor@Sun.COM 	next_tail = (tail + 1) & qsize_msk;
1309517SBill.Taylor@Sun.COM 	if (((tail + hdrmwqes) & qsize_msk) == head) {
1319517SBill.Taylor@Sun.COM 		wq->wq_full = 1;
1329517SBill.Taylor@Sun.COM 	}
1339517SBill.Taylor@Sun.COM 
1349517SBill.Taylor@Sun.COM 	desc = HERMON_QP_SQ_ENTRY(qp, tail);
1359517SBill.Taylor@Sun.COM 
1369517SBill.Taylor@Sun.COM 	nds = wr->wr_nds;
1379517SBill.Taylor@Sun.COM 	sgl = wr->wr_sgl;
1389517SBill.Taylor@Sun.COM 	num_ds = 0;
139*12965SWilliam.Taylor@Oracle.COM 	strong_order = 0;
140*12965SWilliam.Taylor@Oracle.COM 	fc_bits = 0;
141*12965SWilliam.Taylor@Oracle.COM 	cksum = 0;
1429517SBill.Taylor@Sun.COM 
1439517SBill.Taylor@Sun.COM 	/*
1449517SBill.Taylor@Sun.COM 	 * Build a Send or Send_LSO WQE
1459517SBill.Taylor@Sun.COM 	 */
146*12965SWilliam.Taylor@Oracle.COM 	switch (wr->wr_opcode) {
147*12965SWilliam.Taylor@Oracle.COM 	case IBT_WRC_SEND_LSO:
148*12965SWilliam.Taylor@Oracle.COM 		if (wr->wr_trans != IBT_UD_SRV) {
149*12965SWilliam.Taylor@Oracle.COM 			status = IBT_QP_SRV_TYPE_INVALID;
150*12965SWilliam.Taylor@Oracle.COM 			goto done;
151*12965SWilliam.Taylor@Oracle.COM 		}
1529517SBill.Taylor@Sun.COM 		nopcode = HERMON_WQE_SEND_NOPCODE_LSO;
153*12965SWilliam.Taylor@Oracle.COM 		if (wr->wr_flags & IBT_WR_SEND_CKSUM)
154*12965SWilliam.Taylor@Oracle.COM 			cksum = 0x30;
15510633SRajkumar.Sivaprakasam@Sun.COM 		if (wr->wr.ud_lso.lso_hdr_sz > 60) {
15610633SRajkumar.Sivaprakasam@Sun.COM 			nopcode |= (1 << 6);	/* ReRead bit must be set */
15710633SRajkumar.Sivaprakasam@Sun.COM 		}
1589517SBill.Taylor@Sun.COM 		dest = wr->wr.ud_lso.lso_ud_dest;
1599517SBill.Taylor@Sun.COM 		ah = (hermon_ahhdl_t)dest->ud_ah;
1609517SBill.Taylor@Sun.COM 		if (ah == NULL) {
1619517SBill.Taylor@Sun.COM 			status = IBT_AH_HDL_INVALID;
1629517SBill.Taylor@Sun.COM 			goto done;
1639517SBill.Taylor@Sun.COM 		}
164*12965SWilliam.Taylor@Oracle.COM 		ud = (hermon_hw_snd_wqe_ud_t *)((uintptr_t)desc +
165*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_ctrl_t));
166*12965SWilliam.Taylor@Oracle.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)ud +
167*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_ud_t));
1689517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_UD(qp, ud, ah, dest);
1699517SBill.Taylor@Sun.COM 
1709517SBill.Taylor@Sun.COM 		total_len = (4 + 0xf + wr->wr.ud_lso.lso_hdr_sz) & ~0xf;
1719517SBill.Taylor@Sun.COM 		if ((uintptr_t)ds + total_len + (nds * 16) >
1729517SBill.Taylor@Sun.COM 		    (uintptr_t)desc + (1 << qp->qp_sq_log_wqesz)) {
1739517SBill.Taylor@Sun.COM 			status = IBT_QP_SGL_LEN_INVALID;
1749517SBill.Taylor@Sun.COM 			goto done;
1759517SBill.Taylor@Sun.COM 		}
17610633SRajkumar.Sivaprakasam@Sun.COM 		old_ds = ds;
17710633SRajkumar.Sivaprakasam@Sun.COM 		bcopy(wr->wr.ud_lso.lso_hdr, (uint32_t *)old_ds + 1,
1789517SBill.Taylor@Sun.COM 		    wr->wr.ud_lso.lso_hdr_sz);
1799517SBill.Taylor@Sun.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)ds + total_len);
18010633SRajkumar.Sivaprakasam@Sun.COM 		i = 0;
181*12965SWilliam.Taylor@Oracle.COM 		break;
182*12965SWilliam.Taylor@Oracle.COM 
183*12965SWilliam.Taylor@Oracle.COM 	case IBT_WRC_SEND:
184*12965SWilliam.Taylor@Oracle.COM 		nopcode = HERMON_WQE_SEND_NOPCODE_SEND;
185*12965SWilliam.Taylor@Oracle.COM 		if (qp->qp_serv_type == HERMON_QP_UD) {
186*12965SWilliam.Taylor@Oracle.COM 			if (wr->wr_trans != IBT_UD_SRV) {
187*12965SWilliam.Taylor@Oracle.COM 				status = IBT_QP_SRV_TYPE_INVALID;
188*12965SWilliam.Taylor@Oracle.COM 				goto done;
189*12965SWilliam.Taylor@Oracle.COM 			}
190*12965SWilliam.Taylor@Oracle.COM 			if (wr->wr_flags & IBT_WR_SEND_CKSUM)
191*12965SWilliam.Taylor@Oracle.COM 				cksum = 0x30;
192*12965SWilliam.Taylor@Oracle.COM 			dest = wr->wr.ud.udwr_dest;
193*12965SWilliam.Taylor@Oracle.COM 		} else if (qp->qp_serv_type == HERMON_QP_RFCI) {
194*12965SWilliam.Taylor@Oracle.COM 			if (wr->wr_trans != IBT_RFCI_SRV) {
195*12965SWilliam.Taylor@Oracle.COM 				status = IBT_QP_SRV_TYPE_INVALID;
196*12965SWilliam.Taylor@Oracle.COM 				goto done;
197*12965SWilliam.Taylor@Oracle.COM 			}
198*12965SWilliam.Taylor@Oracle.COM 			rfci = &wr->wr.fc.rfci_send;
199*12965SWilliam.Taylor@Oracle.COM 			if ((wr->wr_flags & IBT_WR_SEND_FC_CRC) != 0) {
200*12965SWilliam.Taylor@Oracle.COM 				nopcode |= (rfci->rfci_eof << 16);
201*12965SWilliam.Taylor@Oracle.COM 				fc_bits = 0x40;	/* set FCRC */
202*12965SWilliam.Taylor@Oracle.COM 			}
203*12965SWilliam.Taylor@Oracle.COM 			dest = rfci->rfci_dest;
204*12965SWilliam.Taylor@Oracle.COM 		} else {
205*12965SWilliam.Taylor@Oracle.COM 			status = IBT_QP_OP_TYPE_INVALID;
206*12965SWilliam.Taylor@Oracle.COM 			goto done;
207*12965SWilliam.Taylor@Oracle.COM 		}
2089517SBill.Taylor@Sun.COM 		if (wr->wr_flags & IBT_WR_SEND_IMMED) {
209*12965SWilliam.Taylor@Oracle.COM 			/* "|=" changes 0xa to 0xb without touching FCEOF */
210*12965SWilliam.Taylor@Oracle.COM 			nopcode |= HERMON_WQE_SEND_NOPCODE_SENDI;
2119517SBill.Taylor@Sun.COM 			immed_data = wr->wr.ud.udwr_immed;
2129517SBill.Taylor@Sun.COM 		}
2139517SBill.Taylor@Sun.COM 		ah = (hermon_ahhdl_t)dest->ud_ah;
2149517SBill.Taylor@Sun.COM 		if (ah == NULL) {
2159517SBill.Taylor@Sun.COM 			status = IBT_AH_HDL_INVALID;
2169517SBill.Taylor@Sun.COM 			goto done;
2179517SBill.Taylor@Sun.COM 		}
218*12965SWilliam.Taylor@Oracle.COM 		ud = (hermon_hw_snd_wqe_ud_t *)((uintptr_t)desc +
219*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_ctrl_t));
220*12965SWilliam.Taylor@Oracle.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)ud +
221*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_ud_t));
2229517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_UD(qp, ud, ah, dest);
2239517SBill.Taylor@Sun.COM 		i = 0;
224*12965SWilliam.Taylor@Oracle.COM 		break;
225*12965SWilliam.Taylor@Oracle.COM 
226*12965SWilliam.Taylor@Oracle.COM 	case IBT_WRC_INIT_SEND_FCMD:
227*12965SWilliam.Taylor@Oracle.COM 		if (qp->qp_serv_type != HERMON_QP_FCMND) {
228*12965SWilliam.Taylor@Oracle.COM 			status = IBT_QP_OP_TYPE_INVALID;
229*12965SWilliam.Taylor@Oracle.COM 			goto done;
230*12965SWilliam.Taylor@Oracle.COM 		}
231*12965SWilliam.Taylor@Oracle.COM 		if (wr->wr_trans != IBT_FCMD_SRV) {
232*12965SWilliam.Taylor@Oracle.COM 			status = IBT_QP_SRV_TYPE_INVALID;
233*12965SWilliam.Taylor@Oracle.COM 			goto done;
234*12965SWilliam.Taylor@Oracle.COM 		}
235*12965SWilliam.Taylor@Oracle.COM 		nopcode = HERMON_WQE_FCP_OPCODE_INIT_AND_SEND;
236*12965SWilliam.Taylor@Oracle.COM 		is = wr->wr.fc.fc_is;
237*12965SWilliam.Taylor@Oracle.COM 		dest = is->is_ctl.fc_dest;
238*12965SWilliam.Taylor@Oracle.COM 		ah = (hermon_ahhdl_t)dest->ud_ah;
239*12965SWilliam.Taylor@Oracle.COM 		if (ah == NULL) {
240*12965SWilliam.Taylor@Oracle.COM 			status = IBT_AH_HDL_INVALID;
241*12965SWilliam.Taylor@Oracle.COM 			goto done;
242*12965SWilliam.Taylor@Oracle.COM 		}
243*12965SWilliam.Taylor@Oracle.COM 		ud = (hermon_hw_snd_wqe_ud_t *)((uintptr_t)desc +
244*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_ctrl_t));
245*12965SWilliam.Taylor@Oracle.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)ud +
246*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_ud_t));
247*12965SWilliam.Taylor@Oracle.COM 		HERMON_WQE_BUILD_UD(qp, ud, ah, dest);
248*12965SWilliam.Taylor@Oracle.COM 		old_ds = ds;
249*12965SWilliam.Taylor@Oracle.COM 		/* move ds beyond the FCP-3 Init Segment */
250*12965SWilliam.Taylor@Oracle.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)ds + 0x10);
251*12965SWilliam.Taylor@Oracle.COM 		i = 0;
252*12965SWilliam.Taylor@Oracle.COM 		break;
253*12965SWilliam.Taylor@Oracle.COM 
254*12965SWilliam.Taylor@Oracle.COM 	case IBT_WRC_FAST_REG_PMR:
255*12965SWilliam.Taylor@Oracle.COM 	{
256*12965SWilliam.Taylor@Oracle.COM 		hermon_hw_snd_wqe_frwr_t	*frwr;
257*12965SWilliam.Taylor@Oracle.COM 
258*12965SWilliam.Taylor@Oracle.COM 		if (qp->qp_serv_type != HERMON_QP_FCMND) {
259*12965SWilliam.Taylor@Oracle.COM 			status = IBT_QP_OP_TYPE_INVALID;
260*12965SWilliam.Taylor@Oracle.COM 			goto done;
261*12965SWilliam.Taylor@Oracle.COM 		}
262*12965SWilliam.Taylor@Oracle.COM 		if (wr->wr_trans != IBT_FCMD_SRV) {
263*12965SWilliam.Taylor@Oracle.COM 			status = IBT_QP_SRV_TYPE_INVALID;
264*12965SWilliam.Taylor@Oracle.COM 			goto done;
265*12965SWilliam.Taylor@Oracle.COM 		}
266*12965SWilliam.Taylor@Oracle.COM 		nopcode = HERMON_WQE_SEND_NOPCODE_FRWR;
267*12965SWilliam.Taylor@Oracle.COM 		frwr = (hermon_hw_snd_wqe_frwr_t *)((uintptr_t)desc +
268*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_ctrl_t));
269*12965SWilliam.Taylor@Oracle.COM 		HERMON_WQE_BUILD_FRWR(qp, frwr, wr->wr.fc.reg_pmr);
270*12965SWilliam.Taylor@Oracle.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)frwr +
271*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_frwr_t));
272*12965SWilliam.Taylor@Oracle.COM 		nds = 0;
273*12965SWilliam.Taylor@Oracle.COM 		strong_order = 0x80;
274*12965SWilliam.Taylor@Oracle.COM 		break;
275*12965SWilliam.Taylor@Oracle.COM 	}
276*12965SWilliam.Taylor@Oracle.COM 
277*12965SWilliam.Taylor@Oracle.COM #if 0
278*12965SWilliam.Taylor@Oracle.COM 	/* firmware does not support this */
279*12965SWilliam.Taylor@Oracle.COM 	case IBT_WRC_LOCAL_INVALIDATE:
280*12965SWilliam.Taylor@Oracle.COM 	{
281*12965SWilliam.Taylor@Oracle.COM 		hermon_hw_snd_wqe_local_inv_t	*li;
282*12965SWilliam.Taylor@Oracle.COM 
283*12965SWilliam.Taylor@Oracle.COM 		if (qp->qp_serv_type != HERMON_QP_FCMND) {
284*12965SWilliam.Taylor@Oracle.COM 			status = IBT_QP_OP_TYPE_INVALID;
285*12965SWilliam.Taylor@Oracle.COM 			goto done;
286*12965SWilliam.Taylor@Oracle.COM 		}
287*12965SWilliam.Taylor@Oracle.COM 		if (wr->wr_trans != IBT_FCMD_SRV) {
288*12965SWilliam.Taylor@Oracle.COM 			status = IBT_QP_SRV_TYPE_INVALID;
289*12965SWilliam.Taylor@Oracle.COM 			goto done;
290*12965SWilliam.Taylor@Oracle.COM 		}
291*12965SWilliam.Taylor@Oracle.COM 		nopcode = HERMON_WQE_SEND_NOPCODE_LCL_INV;
292*12965SWilliam.Taylor@Oracle.COM 		li = (hermon_hw_snd_wqe_local_inv_t *)((uintptr_t)desc +
293*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_ctrl_t));
294*12965SWilliam.Taylor@Oracle.COM 		HERMON_WQE_BUILD_LI(qp, li, wr->wr.fc.li);
295*12965SWilliam.Taylor@Oracle.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)li +
296*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_local_inv_t));
297*12965SWilliam.Taylor@Oracle.COM 		nds = 0;
298*12965SWilliam.Taylor@Oracle.COM 		strong_order = 0x80;
299*12965SWilliam.Taylor@Oracle.COM 		break;
300*12965SWilliam.Taylor@Oracle.COM 	}
301*12965SWilliam.Taylor@Oracle.COM #endif
302*12965SWilliam.Taylor@Oracle.COM 	default:
3039517SBill.Taylor@Sun.COM 		status = IBT_QP_OP_TYPE_INVALID;
3049517SBill.Taylor@Sun.COM 		goto done;
3059517SBill.Taylor@Sun.COM 	}
3069517SBill.Taylor@Sun.COM 
3079517SBill.Taylor@Sun.COM 	if (nds > qp->qp_sq_sgl) {
3089517SBill.Taylor@Sun.COM 		status = IBT_QP_SGL_LEN_INVALID;
3099517SBill.Taylor@Sun.COM 		goto done;
3109517SBill.Taylor@Sun.COM 	}
3119517SBill.Taylor@Sun.COM 	for (last_ds = num_ds, j = i; j < nds; j++) {
3129517SBill.Taylor@Sun.COM 		if (sgl[j].ds_len != 0)
3139517SBill.Taylor@Sun.COM 			last_ds++;	/* real last ds of wqe to fill */
3149517SBill.Taylor@Sun.COM 	}
3159517SBill.Taylor@Sun.COM 	desc_sz = ((uintptr_t)&ds[last_ds] - (uintptr_t)desc) >> 0x4;
3169517SBill.Taylor@Sun.COM 	for (j = nds; --j >= i; ) {
3179517SBill.Taylor@Sun.COM 		if (sgl[j].ds_len == 0) {
3189517SBill.Taylor@Sun.COM 			continue;
3199517SBill.Taylor@Sun.COM 		}
3209517SBill.Taylor@Sun.COM 
3219517SBill.Taylor@Sun.COM 		/*
3229517SBill.Taylor@Sun.COM 		 * Fill in the Data Segment(s) for the current WQE, using the
3239517SBill.Taylor@Sun.COM 		 * information contained in the scatter-gather list of the
3249517SBill.Taylor@Sun.COM 		 * work request.
3259517SBill.Taylor@Sun.COM 		 */
3269517SBill.Taylor@Sun.COM 		last_ds--;
3279517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_DATA_SEG_SEND(&ds[last_ds], &sgl[j]);
3289517SBill.Taylor@Sun.COM 	}
3299517SBill.Taylor@Sun.COM 
33010633SRajkumar.Sivaprakasam@Sun.COM 	membar_producer();
33110633SRajkumar.Sivaprakasam@Sun.COM 
33210633SRajkumar.Sivaprakasam@Sun.COM 	if (wr->wr_opcode == IBT_WRC_SEND_LSO) {
33310633SRajkumar.Sivaprakasam@Sun.COM 		HERMON_WQE_BUILD_LSO(qp, old_ds, wr->wr.ud_lso.lso_mss,
33410633SRajkumar.Sivaprakasam@Sun.COM 		    wr->wr.ud_lso.lso_hdr_sz);
335*12965SWilliam.Taylor@Oracle.COM 	} else if (wr->wr_opcode == IBT_WRC_INIT_SEND_FCMD) {
336*12965SWilliam.Taylor@Oracle.COM 		/* This sits in the STAMP, so must be set after setting SGL */
337*12965SWilliam.Taylor@Oracle.COM 		HERMON_WQE_BUILD_FCP3_INIT(old_ds, is->is_ctl.fc_frame_ctrl,
338*12965SWilliam.Taylor@Oracle.COM 		    is->is_cs_priority, is->is_tx_seq_id, is->is_fc_mtu,
339*12965SWilliam.Taylor@Oracle.COM 		    is->is_dest_id, is->is_op, is->is_rem_exch,
340*12965SWilliam.Taylor@Oracle.COM 		    is->is_exch_qp_idx);
341*12965SWilliam.Taylor@Oracle.COM 
342*12965SWilliam.Taylor@Oracle.COM 		/* The following will be used in HERMON_WQE_SET_CTRL_SEGMENT */
343*12965SWilliam.Taylor@Oracle.COM 		/* SIT bit in FCP-3 ctrl segment */
344*12965SWilliam.Taylor@Oracle.COM 		desc_sz |= (is->is_ctl.fc_frame_ctrl & IBT_FCTL_SIT) ? 0x80 : 0;
345*12965SWilliam.Taylor@Oracle.COM 		/* LS bit in FCP-3 ctrl segment */
346*12965SWilliam.Taylor@Oracle.COM 		fc_bits |= (is->is_ctl.fc_frame_ctrl & IBT_FCTL_LAST_SEQ) ?
347*12965SWilliam.Taylor@Oracle.COM 		    0x10000 : 0;
348*12965SWilliam.Taylor@Oracle.COM 		fc_bits |= ((is->is_ctl.fc_routing_ctrl & 0xF) << 20) |
349*12965SWilliam.Taylor@Oracle.COM 		    (is->is_ctl.fc_seq_id << 24);
350*12965SWilliam.Taylor@Oracle.COM 		immed_data = is->is_ctl.fc_parameter;
35110633SRajkumar.Sivaprakasam@Sun.COM 	}
35210633SRajkumar.Sivaprakasam@Sun.COM 
3539517SBill.Taylor@Sun.COM 	fence = (wr->wr_flags & IBT_WR_SEND_FENCE) ? 1 : 0;
3549517SBill.Taylor@Sun.COM 
3559517SBill.Taylor@Sun.COM 	signaled_dbd = ((qp->qp_sq_sigtype == HERMON_QP_SQ_ALL_SIGNALED) ||
356*12965SWilliam.Taylor@Oracle.COM 	    (wr->wr_flags & IBT_WR_SEND_SIGNAL)) ? 0xC : 0;
3579517SBill.Taylor@Sun.COM 
358*12965SWilliam.Taylor@Oracle.COM 	solicited = (wr->wr_flags & IBT_WR_SEND_SOLICIT) ? 0x2 : 0;
3599517SBill.Taylor@Sun.COM 
3609517SBill.Taylor@Sun.COM 	HERMON_WQE_SET_CTRL_SEGMENT(desc, desc_sz, fence, immed_data,
361*12965SWilliam.Taylor@Oracle.COM 	    solicited, signaled_dbd, cksum, qp, strong_order, fc_bits);
3629517SBill.Taylor@Sun.COM 
3639517SBill.Taylor@Sun.COM 	wq->wq_wrid[tail] = wr->wr_id;
3649517SBill.Taylor@Sun.COM 
3659517SBill.Taylor@Sun.COM 	tail = next_tail;
3669517SBill.Taylor@Sun.COM 
3679517SBill.Taylor@Sun.COM 	/* Update some of the state in the QP */
3689517SBill.Taylor@Sun.COM 	wq->wq_tail = tail;
3699517SBill.Taylor@Sun.COM 
3709517SBill.Taylor@Sun.COM 	membar_producer();
3719517SBill.Taylor@Sun.COM 
3729517SBill.Taylor@Sun.COM 	/* Now set the ownership bit and opcode (first dword). */
3739517SBill.Taylor@Sun.COM 	HERMON_SET_SEND_WQE_OWNER(qp, (uint32_t *)desc, nopcode);
3749517SBill.Taylor@Sun.COM 
3759517SBill.Taylor@Sun.COM 	posted_cnt++;
3769517SBill.Taylor@Sun.COM 	if (--num_wr > 0) {
3779517SBill.Taylor@Sun.COM 		/* do the invalidate of the headroom */
3789517SBill.Taylor@Sun.COM 		wqe_start = (uint32_t *)HERMON_QP_SQ_ENTRY(qp,
3799517SBill.Taylor@Sun.COM 		    (tail + hdrmwqes) & qsize_msk);
3809517SBill.Taylor@Sun.COM 		for (i = 16; i < sectperwqe; i += 16) {
3819517SBill.Taylor@Sun.COM 			wqe_start[i] = 0xFFFFFFFF;
3829517SBill.Taylor@Sun.COM 		}
3839517SBill.Taylor@Sun.COM 
3849517SBill.Taylor@Sun.COM 		wr++;
3859517SBill.Taylor@Sun.COM 		goto post_next;
3869517SBill.Taylor@Sun.COM 	}
3879517SBill.Taylor@Sun.COM done:
3889517SBill.Taylor@Sun.COM 	if (posted_cnt != 0) {
3899517SBill.Taylor@Sun.COM 		ddi_acc_handle_t uarhdl = hermon_get_uarhdl(state);
3909517SBill.Taylor@Sun.COM 
3919517SBill.Taylor@Sun.COM 		membar_producer();
3929517SBill.Taylor@Sun.COM 
3939517SBill.Taylor@Sun.COM 		/* the FMA retry loop starts for Hermon doorbell register. */
3949517SBill.Taylor@Sun.COM 		hermon_pio_start(state, uarhdl, pio_error, fm_loop_cnt,
3959517SBill.Taylor@Sun.COM 		    fm_status, fm_test_num);
3969517SBill.Taylor@Sun.COM 
3979517SBill.Taylor@Sun.COM 		HERMON_UAR_DOORBELL(state, uarhdl,
3989517SBill.Taylor@Sun.COM 		    (uint64_t *)(void *)&state->hs_uar->send,
3999517SBill.Taylor@Sun.COM 		    (uint64_t)qp->qp_ring);
4009517SBill.Taylor@Sun.COM 
4019517SBill.Taylor@Sun.COM 		/* the FMA retry loop ends. */
4029517SBill.Taylor@Sun.COM 		hermon_pio_end(state, uarhdl, pio_error, fm_loop_cnt,
4039517SBill.Taylor@Sun.COM 		    fm_status, fm_test_num);
4049517SBill.Taylor@Sun.COM 
4059517SBill.Taylor@Sun.COM 		/* do the invalidate of the headroom */
4069517SBill.Taylor@Sun.COM 		wqe_start = (uint32_t *)HERMON_QP_SQ_ENTRY(qp,
4079517SBill.Taylor@Sun.COM 		    (tail + hdrmwqes) & qsize_msk);
4089517SBill.Taylor@Sun.COM 		for (i = 16; i < sectperwqe; i += 16) {
4099517SBill.Taylor@Sun.COM 			wqe_start[i] = 0xFFFFFFFF;
4109517SBill.Taylor@Sun.COM 		}
4119517SBill.Taylor@Sun.COM 	}
4129517SBill.Taylor@Sun.COM 	if (num_posted != NULL)
4139517SBill.Taylor@Sun.COM 		*num_posted = posted_cnt;
4149517SBill.Taylor@Sun.COM 
4159517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_sq_lock);
4169517SBill.Taylor@Sun.COM 
4179517SBill.Taylor@Sun.COM 	return (status);
4189517SBill.Taylor@Sun.COM 
4199517SBill.Taylor@Sun.COM pio_error:
4209517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_sq_lock);
4219517SBill.Taylor@Sun.COM 	hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
4229517SBill.Taylor@Sun.COM 	return (ibc_get_ci_failure(0));
4239517SBill.Taylor@Sun.COM }
4249517SBill.Taylor@Sun.COM 
4259517SBill.Taylor@Sun.COM static int
hermon_post_send_rc(hermon_state_t * state,hermon_qphdl_t qp,ibt_send_wr_t * wr,uint_t num_wr,uint_t * num_posted)4269517SBill.Taylor@Sun.COM hermon_post_send_rc(hermon_state_t *state, hermon_qphdl_t qp,
4279517SBill.Taylor@Sun.COM     ibt_send_wr_t *wr, uint_t num_wr, uint_t *num_posted)
4289517SBill.Taylor@Sun.COM {
4299517SBill.Taylor@Sun.COM 	uint64_t			*desc;
4309517SBill.Taylor@Sun.COM 	hermon_workq_hdr_t		*wq;
4319517SBill.Taylor@Sun.COM 	uint32_t			desc_sz;
4329517SBill.Taylor@Sun.COM 	uint32_t			signaled_dbd, solicited;
4339517SBill.Taylor@Sun.COM 	uint32_t			head, tail, next_tail, qsize_msk;
4349517SBill.Taylor@Sun.COM 	uint32_t			hdrmwqes;
4359517SBill.Taylor@Sun.COM 	int				status;
4369517SBill.Taylor@Sun.COM 	uint32_t			nopcode, fence, immed_data = 0;
4379517SBill.Taylor@Sun.COM 	hermon_hw_snd_wqe_remaddr_t	*rc;
4389517SBill.Taylor@Sun.COM 	hermon_hw_snd_wqe_atomic_t	*at;
4399517SBill.Taylor@Sun.COM 	hermon_hw_snd_wqe_bind_t	*bn;
440*12965SWilliam.Taylor@Oracle.COM 	hermon_hw_snd_wqe_frwr_t	*frwr;
441*12965SWilliam.Taylor@Oracle.COM 	hermon_hw_snd_wqe_local_inv_t	*li;
4429517SBill.Taylor@Sun.COM 	hermon_hw_wqe_sgl_t		*ds;
4439517SBill.Taylor@Sun.COM 	ibt_wr_ds_t			*sgl;
444*12965SWilliam.Taylor@Oracle.COM 	int				nds;
4459517SBill.Taylor@Sun.COM 	int				i, last_ds, num_ds;
4469517SBill.Taylor@Sun.COM 	uint32_t			*wqe_start;
4479517SBill.Taylor@Sun.COM 	int				sectperwqe;
4489517SBill.Taylor@Sun.COM 	uint_t				posted_cnt = 0;
449*12965SWilliam.Taylor@Oracle.COM 	int				strong_order;
45011972SBill.Taylor@Sun.COM 	int				print_rdma;
45111972SBill.Taylor@Sun.COM 	int				rlen;
45211972SBill.Taylor@Sun.COM 	uint32_t			rkey;
45311972SBill.Taylor@Sun.COM 	uint64_t			raddr;
4549517SBill.Taylor@Sun.COM 
4559517SBill.Taylor@Sun.COM 	/* initialize the FMA retry loop */
4569517SBill.Taylor@Sun.COM 	hermon_pio_init(fm_loop_cnt, fm_status, fm_test_num);
4579517SBill.Taylor@Sun.COM 
4589517SBill.Taylor@Sun.COM 	ASSERT(MUTEX_HELD(&qp->qp_sq_lock));
4599517SBill.Taylor@Sun.COM 	_NOTE(LOCK_RELEASED_AS_SIDE_EFFECT(&qp->qp_sq_lock))
4609517SBill.Taylor@Sun.COM 
4619517SBill.Taylor@Sun.COM 	/* Save away some initial QP state */
4629517SBill.Taylor@Sun.COM 	wq = qp->qp_sq_wqhdr;
4639517SBill.Taylor@Sun.COM 	qsize_msk = wq->wq_mask;
4649517SBill.Taylor@Sun.COM 	hdrmwqes  = qp->qp_sq_hdrmwqes;		/* in WQEs  */
4659517SBill.Taylor@Sun.COM 	sectperwqe = 1 << (qp->qp_sq_log_wqesz - 2);
4669517SBill.Taylor@Sun.COM 
4679517SBill.Taylor@Sun.COM 	tail	  = wq->wq_tail;
4689517SBill.Taylor@Sun.COM 	head	  = wq->wq_head;
4699517SBill.Taylor@Sun.COM 	status	  = DDI_SUCCESS;
4709517SBill.Taylor@Sun.COM 
4719517SBill.Taylor@Sun.COM post_next:
47211972SBill.Taylor@Sun.COM 	print_rdma = 0;
47311972SBill.Taylor@Sun.COM 	rlen = 0;
474*12965SWilliam.Taylor@Oracle.COM 	strong_order = 0;
47511972SBill.Taylor@Sun.COM 
4769517SBill.Taylor@Sun.COM 	/*
4779517SBill.Taylor@Sun.COM 	 * Check for "queue full" condition.  If the queue
4789517SBill.Taylor@Sun.COM 	 * is already full, then no more WQEs can be posted.
4799517SBill.Taylor@Sun.COM 	 * So break out, ring a doorbell (if necessary) and
4809517SBill.Taylor@Sun.COM 	 * return an error
4819517SBill.Taylor@Sun.COM 	 */
4829517SBill.Taylor@Sun.COM 	if (wq->wq_full != 0) {
4839517SBill.Taylor@Sun.COM 		status = IBT_QP_FULL;
4849517SBill.Taylor@Sun.COM 		goto done;
4859517SBill.Taylor@Sun.COM 	}
4869517SBill.Taylor@Sun.COM 	next_tail = (tail + 1) & qsize_msk;
4879517SBill.Taylor@Sun.COM 	if (((tail + hdrmwqes) & qsize_msk) == head) {
4889517SBill.Taylor@Sun.COM 		wq->wq_full = 1;
4899517SBill.Taylor@Sun.COM 	}
4909517SBill.Taylor@Sun.COM 
4919517SBill.Taylor@Sun.COM 	desc = HERMON_QP_SQ_ENTRY(qp, tail);
4929517SBill.Taylor@Sun.COM 
4939517SBill.Taylor@Sun.COM 	ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)desc +
4949517SBill.Taylor@Sun.COM 	    sizeof (hermon_hw_snd_wqe_ctrl_t));
4959517SBill.Taylor@Sun.COM 	nds = wr->wr_nds;
4969517SBill.Taylor@Sun.COM 	sgl = wr->wr_sgl;
4979517SBill.Taylor@Sun.COM 	num_ds = 0;
498*12965SWilliam.Taylor@Oracle.COM 	if (wr->wr_trans != IBT_RC_SRV) {
499*12965SWilliam.Taylor@Oracle.COM 		status = IBT_QP_SRV_TYPE_INVALID;
500*12965SWilliam.Taylor@Oracle.COM 		goto done;
501*12965SWilliam.Taylor@Oracle.COM 	}
5029517SBill.Taylor@Sun.COM 
5039517SBill.Taylor@Sun.COM 	/*
5049517SBill.Taylor@Sun.COM 	 * Validate the operation type.  For RC requests, we allow
5059517SBill.Taylor@Sun.COM 	 * "Send", "RDMA Read", "RDMA Write", various "Atomic"
5069517SBill.Taylor@Sun.COM 	 * operations, and memory window "Bind"
5079517SBill.Taylor@Sun.COM 	 */
5089517SBill.Taylor@Sun.COM 	switch (wr->wr_opcode) {
5099517SBill.Taylor@Sun.COM 	default:
5109517SBill.Taylor@Sun.COM 		status = IBT_QP_OP_TYPE_INVALID;
5119517SBill.Taylor@Sun.COM 		goto done;
5129517SBill.Taylor@Sun.COM 
5139517SBill.Taylor@Sun.COM 	case IBT_WRC_SEND:
514*12965SWilliam.Taylor@Oracle.COM 		if (wr->wr_flags & IBT_WR_SEND_REMOTE_INVAL) {
515*12965SWilliam.Taylor@Oracle.COM 			nopcode = HERMON_WQE_SEND_NOPCODE_SND_INV;
516*12965SWilliam.Taylor@Oracle.COM 			immed_data = wr->wr.rc.rcwr.send_inval;
517*12965SWilliam.Taylor@Oracle.COM 		} else if (wr->wr_flags & IBT_WR_SEND_IMMED) {
5189517SBill.Taylor@Sun.COM 			nopcode = HERMON_WQE_SEND_NOPCODE_SENDI;
5199517SBill.Taylor@Sun.COM 			immed_data = wr->wr.rc.rcwr.send_immed;
5209517SBill.Taylor@Sun.COM 		} else {
5219517SBill.Taylor@Sun.COM 			nopcode = HERMON_WQE_SEND_NOPCODE_SEND;
5229517SBill.Taylor@Sun.COM 		}
5239517SBill.Taylor@Sun.COM 		break;
5249517SBill.Taylor@Sun.COM 
5259517SBill.Taylor@Sun.COM 	/*
5269517SBill.Taylor@Sun.COM 	 * If this is an RDMA Read or RDMA Write request, then fill
5279517SBill.Taylor@Sun.COM 	 * in the "Remote Address" header fields.
5289517SBill.Taylor@Sun.COM 	 */
5299517SBill.Taylor@Sun.COM 	case IBT_WRC_RDMAW:
5309517SBill.Taylor@Sun.COM 		if (wr->wr_flags & IBT_WR_SEND_IMMED) {
5319517SBill.Taylor@Sun.COM 			nopcode = HERMON_WQE_SEND_NOPCODE_RDMAWI;
5329517SBill.Taylor@Sun.COM 			immed_data = wr->wr.rc.rcwr.rdma.rdma_immed;
5339517SBill.Taylor@Sun.COM 		} else {
5349517SBill.Taylor@Sun.COM 			nopcode = HERMON_WQE_SEND_NOPCODE_RDMAW;
5359517SBill.Taylor@Sun.COM 		}
5369517SBill.Taylor@Sun.COM 		/* FALLTHROUGH */
5379517SBill.Taylor@Sun.COM 	case IBT_WRC_RDMAR:
5389517SBill.Taylor@Sun.COM 		if (wr->wr_opcode == IBT_WRC_RDMAR)
5399517SBill.Taylor@Sun.COM 			nopcode = HERMON_WQE_SEND_NOPCODE_RDMAR;
5409517SBill.Taylor@Sun.COM 		rc = (hermon_hw_snd_wqe_remaddr_t *)((uintptr_t)desc +
5419517SBill.Taylor@Sun.COM 		    sizeof (hermon_hw_snd_wqe_ctrl_t));
5429517SBill.Taylor@Sun.COM 
5439517SBill.Taylor@Sun.COM 		/*
5449517SBill.Taylor@Sun.COM 		 * Build the Remote Address Segment for the WQE, using
5459517SBill.Taylor@Sun.COM 		 * the information from the RC work request.
5469517SBill.Taylor@Sun.COM 		 */
5479517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_REMADDR(qp, rc, &wr->wr.rc.rcwr.rdma);
5489517SBill.Taylor@Sun.COM 
54911972SBill.Taylor@Sun.COM 		if (hermon_rdma_debug) {
55011972SBill.Taylor@Sun.COM 			print_rdma = hermon_rdma_debug;
55111972SBill.Taylor@Sun.COM 			rkey = wr->wr.rc.rcwr.rdma.rdma_rkey;
55211972SBill.Taylor@Sun.COM 			raddr = wr->wr.rc.rcwr.rdma.rdma_raddr;
55311972SBill.Taylor@Sun.COM 		}
55411972SBill.Taylor@Sun.COM 
5559517SBill.Taylor@Sun.COM 		/* Update "ds" for filling in Data Segments (below) */
5569517SBill.Taylor@Sun.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)rc +
5579517SBill.Taylor@Sun.COM 		    sizeof (hermon_hw_snd_wqe_remaddr_t));
5589517SBill.Taylor@Sun.COM 		break;
5599517SBill.Taylor@Sun.COM 
5609517SBill.Taylor@Sun.COM 	/*
5619517SBill.Taylor@Sun.COM 	 * If this is one of the Atomic type operations (i.e
5629517SBill.Taylor@Sun.COM 	 * Compare-Swap or Fetch-Add), then fill in both the "Remote
5639517SBill.Taylor@Sun.COM 	 * Address" header fields and the "Atomic" header fields.
5649517SBill.Taylor@Sun.COM 	 */
5659517SBill.Taylor@Sun.COM 	case IBT_WRC_CSWAP:
5669517SBill.Taylor@Sun.COM 		nopcode = HERMON_WQE_SEND_NOPCODE_ATMCS;
5679517SBill.Taylor@Sun.COM 		/* FALLTHROUGH */
5689517SBill.Taylor@Sun.COM 	case IBT_WRC_FADD:
5699517SBill.Taylor@Sun.COM 		if (wr->wr_opcode == IBT_WRC_FADD)
5709517SBill.Taylor@Sun.COM 			nopcode = HERMON_WQE_SEND_NOPCODE_ATMFA;
5719517SBill.Taylor@Sun.COM 		rc = (hermon_hw_snd_wqe_remaddr_t *)((uintptr_t)desc +
5729517SBill.Taylor@Sun.COM 		    sizeof (hermon_hw_snd_wqe_ctrl_t));
5739517SBill.Taylor@Sun.COM 		at = (hermon_hw_snd_wqe_atomic_t *)((uintptr_t)rc +
5749517SBill.Taylor@Sun.COM 		    sizeof (hermon_hw_snd_wqe_remaddr_t));
5759517SBill.Taylor@Sun.COM 
5769517SBill.Taylor@Sun.COM 		/*
5779517SBill.Taylor@Sun.COM 		 * Build the Remote Address and Atomic Segments for
5789517SBill.Taylor@Sun.COM 		 * the WQE, using the information from the RC Atomic
5799517SBill.Taylor@Sun.COM 		 * work request.
5809517SBill.Taylor@Sun.COM 		 */
5819517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_RC_ATOMIC_REMADDR(qp, rc, wr);
5829517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_ATOMIC(qp, at, wr->wr.rc.rcwr.atomic);
5839517SBill.Taylor@Sun.COM 
5849517SBill.Taylor@Sun.COM 		/* Update "ds" for filling in Data Segments (below) */
5859517SBill.Taylor@Sun.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)at +
5869517SBill.Taylor@Sun.COM 		    sizeof (hermon_hw_snd_wqe_atomic_t));
5879517SBill.Taylor@Sun.COM 
5889517SBill.Taylor@Sun.COM 		/*
5899517SBill.Taylor@Sun.COM 		 * Update "nds" and "sgl" because Atomic requests have
5909517SBill.Taylor@Sun.COM 		 * only a single Data Segment.
5919517SBill.Taylor@Sun.COM 		 */
5929517SBill.Taylor@Sun.COM 		nds = 1;
5939517SBill.Taylor@Sun.COM 		sgl = wr->wr_sgl;
5949517SBill.Taylor@Sun.COM 		break;
5959517SBill.Taylor@Sun.COM 
5969517SBill.Taylor@Sun.COM 	/*
5979517SBill.Taylor@Sun.COM 	 * If this is memory window Bind operation, then we call the
5989517SBill.Taylor@Sun.COM 	 * hermon_wr_bind_check() routine to validate the request and
5999517SBill.Taylor@Sun.COM 	 * to generate the updated RKey.  If this is successful, then
6009517SBill.Taylor@Sun.COM 	 * we fill in the WQE's "Bind" header fields.
6019517SBill.Taylor@Sun.COM 	 */
6029517SBill.Taylor@Sun.COM 	case IBT_WRC_BIND:
6039517SBill.Taylor@Sun.COM 		nopcode = HERMON_WQE_SEND_NOPCODE_BIND;
6049517SBill.Taylor@Sun.COM 		status = hermon_wr_bind_check(state, wr);
6059517SBill.Taylor@Sun.COM 		if (status != DDI_SUCCESS)
6069517SBill.Taylor@Sun.COM 			goto done;
6079517SBill.Taylor@Sun.COM 
6089517SBill.Taylor@Sun.COM 		bn = (hermon_hw_snd_wqe_bind_t *)((uintptr_t)desc +
6099517SBill.Taylor@Sun.COM 		    sizeof (hermon_hw_snd_wqe_ctrl_t));
6109517SBill.Taylor@Sun.COM 
6119517SBill.Taylor@Sun.COM 		/*
6129517SBill.Taylor@Sun.COM 		 * Build the Bind Memory Window Segments for the WQE,
6139517SBill.Taylor@Sun.COM 		 * using the information from the RC Bind memory
6149517SBill.Taylor@Sun.COM 		 * window work request.
6159517SBill.Taylor@Sun.COM 		 */
6169517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_BIND(qp, bn, wr->wr.rc.rcwr.bind);
6179517SBill.Taylor@Sun.COM 
6189517SBill.Taylor@Sun.COM 		/*
6199517SBill.Taylor@Sun.COM 		 * Update the "ds" pointer.  Even though the "bind"
6209517SBill.Taylor@Sun.COM 		 * operation requires no SGLs, this is necessary to
6219517SBill.Taylor@Sun.COM 		 * facilitate the correct descriptor size calculations
6229517SBill.Taylor@Sun.COM 		 * (below).
6239517SBill.Taylor@Sun.COM 		 */
6249517SBill.Taylor@Sun.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)bn +
6259517SBill.Taylor@Sun.COM 		    sizeof (hermon_hw_snd_wqe_bind_t));
6269517SBill.Taylor@Sun.COM 		nds = 0;
627*12965SWilliam.Taylor@Oracle.COM 		break;
628*12965SWilliam.Taylor@Oracle.COM 
629*12965SWilliam.Taylor@Oracle.COM 	case IBT_WRC_FAST_REG_PMR:
630*12965SWilliam.Taylor@Oracle.COM 		nopcode = HERMON_WQE_SEND_NOPCODE_FRWR;
631*12965SWilliam.Taylor@Oracle.COM 		frwr = (hermon_hw_snd_wqe_frwr_t *)((uintptr_t)desc +
632*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_ctrl_t));
633*12965SWilliam.Taylor@Oracle.COM 		HERMON_WQE_BUILD_FRWR(qp, frwr, wr->wr.rc.rcwr.reg_pmr);
634*12965SWilliam.Taylor@Oracle.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)frwr +
635*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_frwr_t));
636*12965SWilliam.Taylor@Oracle.COM 		nds = 0;
637*12965SWilliam.Taylor@Oracle.COM 		strong_order = 0x80;
638*12965SWilliam.Taylor@Oracle.COM 		break;
639*12965SWilliam.Taylor@Oracle.COM 
640*12965SWilliam.Taylor@Oracle.COM 	case IBT_WRC_LOCAL_INVALIDATE:
641*12965SWilliam.Taylor@Oracle.COM 		nopcode = HERMON_WQE_SEND_NOPCODE_LCL_INV;
642*12965SWilliam.Taylor@Oracle.COM 		li = (hermon_hw_snd_wqe_local_inv_t *)((uintptr_t)desc +
643*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_ctrl_t));
644*12965SWilliam.Taylor@Oracle.COM 		HERMON_WQE_BUILD_LI(qp, li, wr->wr.rc.rcwr.li);
645*12965SWilliam.Taylor@Oracle.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)li +
646*12965SWilliam.Taylor@Oracle.COM 		    sizeof (hermon_hw_snd_wqe_local_inv_t));
647*12965SWilliam.Taylor@Oracle.COM 		nds = 0;
648*12965SWilliam.Taylor@Oracle.COM 		strong_order = 0x80;
649*12965SWilliam.Taylor@Oracle.COM 		break;
6509517SBill.Taylor@Sun.COM 	}
6519517SBill.Taylor@Sun.COM 
6529517SBill.Taylor@Sun.COM 	/*
6539517SBill.Taylor@Sun.COM 	 * Now fill in the Data Segments (SGL) for the Send WQE based
6549517SBill.Taylor@Sun.COM 	 * on the values setup above (i.e. "sgl", "nds", and the "ds"
6559517SBill.Taylor@Sun.COM 	 * pointer. Start by checking for a valid number of SGL entries
6569517SBill.Taylor@Sun.COM 	 */
6579517SBill.Taylor@Sun.COM 	if (nds > qp->qp_sq_sgl) {
6589517SBill.Taylor@Sun.COM 		status = IBT_QP_SGL_LEN_INVALID;
6599517SBill.Taylor@Sun.COM 		goto done;
6609517SBill.Taylor@Sun.COM 	}
6619517SBill.Taylor@Sun.COM 
6629517SBill.Taylor@Sun.COM 	for (last_ds = num_ds, i = 0; i < nds; i++) {
6639517SBill.Taylor@Sun.COM 		if (sgl[i].ds_len != 0)
6649517SBill.Taylor@Sun.COM 			last_ds++;	/* real last ds of wqe to fill */
6659517SBill.Taylor@Sun.COM 	}
6669517SBill.Taylor@Sun.COM 	desc_sz = ((uintptr_t)&ds[last_ds] - (uintptr_t)desc) >> 0x4;
6679517SBill.Taylor@Sun.COM 	for (i = nds; --i >= 0; ) {
6689517SBill.Taylor@Sun.COM 		if (sgl[i].ds_len == 0) {
6699517SBill.Taylor@Sun.COM 			continue;
6709517SBill.Taylor@Sun.COM 		}
67111972SBill.Taylor@Sun.COM 		rlen += sgl[i].ds_len;
67211972SBill.Taylor@Sun.COM 		if (print_rdma & 0x2)
67311972SBill.Taylor@Sun.COM 			IBTF_DPRINTF_L2("rdma", "post: [%d]: laddr %llx  "
67411972SBill.Taylor@Sun.COM 			    "llen %x", i, sgl[i].ds_va, sgl[i].ds_len);
6759517SBill.Taylor@Sun.COM 
6769517SBill.Taylor@Sun.COM 		/*
6779517SBill.Taylor@Sun.COM 		 * Fill in the Data Segment(s) for the current WQE, using the
6789517SBill.Taylor@Sun.COM 		 * information contained in the scatter-gather list of the
6799517SBill.Taylor@Sun.COM 		 * work request.
6809517SBill.Taylor@Sun.COM 		 */
6819517SBill.Taylor@Sun.COM 		last_ds--;
6829517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_DATA_SEG_SEND(&ds[last_ds], &sgl[i]);
6839517SBill.Taylor@Sun.COM 	}
684*12965SWilliam.Taylor@Oracle.COM 	/* ensure RDMA READ does not exceed HCA limit */
685*12965SWilliam.Taylor@Oracle.COM 	if ((wr->wr_opcode == IBT_WRC_RDMAR) && (desc_sz >
686*12965SWilliam.Taylor@Oracle.COM 	    state->hs_ibtfinfo.hca_attr->hca_conn_rdma_read_sgl_sz + 2)) {
687*12965SWilliam.Taylor@Oracle.COM 		status = IBT_QP_SGL_LEN_INVALID;
688*12965SWilliam.Taylor@Oracle.COM 		goto done;
689*12965SWilliam.Taylor@Oracle.COM 	}
6909517SBill.Taylor@Sun.COM 
69111972SBill.Taylor@Sun.COM 	if (print_rdma & 0x1) {
69211972SBill.Taylor@Sun.COM 		IBTF_DPRINTF_L2("rdma", "post: indx %x  rkey %x  raddr %llx  "
69311972SBill.Taylor@Sun.COM 		    "total len %x", tail, rkey, raddr, rlen);
69411972SBill.Taylor@Sun.COM 	}
69511972SBill.Taylor@Sun.COM 
6969517SBill.Taylor@Sun.COM 	fence = (wr->wr_flags & IBT_WR_SEND_FENCE) ? 1 : 0;
6979517SBill.Taylor@Sun.COM 
6989517SBill.Taylor@Sun.COM 	signaled_dbd = ((qp->qp_sq_sigtype == HERMON_QP_SQ_ALL_SIGNALED) ||
699*12965SWilliam.Taylor@Oracle.COM 	    (wr->wr_flags & IBT_WR_SEND_SIGNAL)) ? 0xC : 0;
7009517SBill.Taylor@Sun.COM 
701*12965SWilliam.Taylor@Oracle.COM 	solicited = (wr->wr_flags & IBT_WR_SEND_SOLICIT) ? 0x2 : 0;
7029517SBill.Taylor@Sun.COM 
7039517SBill.Taylor@Sun.COM 	HERMON_WQE_SET_CTRL_SEGMENT(desc, desc_sz, fence, immed_data, solicited,
704*12965SWilliam.Taylor@Oracle.COM 	    signaled_dbd, 0, qp, strong_order, 0);
7059517SBill.Taylor@Sun.COM 
7069517SBill.Taylor@Sun.COM 	wq->wq_wrid[tail] = wr->wr_id;
7079517SBill.Taylor@Sun.COM 
7089517SBill.Taylor@Sun.COM 	tail = next_tail;
7099517SBill.Taylor@Sun.COM 
7109517SBill.Taylor@Sun.COM 	/* Update some of the state in the QP */
7119517SBill.Taylor@Sun.COM 	wq->wq_tail = tail;
7129517SBill.Taylor@Sun.COM 
7139517SBill.Taylor@Sun.COM 	membar_producer();
7149517SBill.Taylor@Sun.COM 
7159517SBill.Taylor@Sun.COM 	/* Now set the ownership bit of the first one in the chain. */
7169517SBill.Taylor@Sun.COM 	HERMON_SET_SEND_WQE_OWNER(qp, (uint32_t *)desc, nopcode);
7179517SBill.Taylor@Sun.COM 
7189517SBill.Taylor@Sun.COM 	posted_cnt++;
7199517SBill.Taylor@Sun.COM 	if (--num_wr > 0) {
7209517SBill.Taylor@Sun.COM 		/* do the invalidate of the headroom */
7219517SBill.Taylor@Sun.COM 		wqe_start = (uint32_t *)HERMON_QP_SQ_ENTRY(qp,
7229517SBill.Taylor@Sun.COM 		    (tail + hdrmwqes) & qsize_msk);
7239517SBill.Taylor@Sun.COM 		for (i = 16; i < sectperwqe; i += 16) {
7249517SBill.Taylor@Sun.COM 			wqe_start[i] = 0xFFFFFFFF;
7259517SBill.Taylor@Sun.COM 		}
7269517SBill.Taylor@Sun.COM 
7279517SBill.Taylor@Sun.COM 		wr++;
7289517SBill.Taylor@Sun.COM 		goto post_next;
7299517SBill.Taylor@Sun.COM 	}
7309517SBill.Taylor@Sun.COM done:
7319517SBill.Taylor@Sun.COM 
7329517SBill.Taylor@Sun.COM 	if (posted_cnt != 0) {
7339517SBill.Taylor@Sun.COM 		ddi_acc_handle_t uarhdl = hermon_get_uarhdl(state);
7349517SBill.Taylor@Sun.COM 
7359517SBill.Taylor@Sun.COM 		membar_producer();
7369517SBill.Taylor@Sun.COM 
7379517SBill.Taylor@Sun.COM 		/* the FMA retry loop starts for Hermon doorbell register. */
7389517SBill.Taylor@Sun.COM 		hermon_pio_start(state, uarhdl, pio_error, fm_loop_cnt,
7399517SBill.Taylor@Sun.COM 		    fm_status, fm_test_num);
7409517SBill.Taylor@Sun.COM 
7419517SBill.Taylor@Sun.COM 		/* Ring the doorbell */
7429517SBill.Taylor@Sun.COM 		HERMON_UAR_DOORBELL(state, uarhdl,
7439517SBill.Taylor@Sun.COM 		    (uint64_t *)(void *)&state->hs_uar->send,
7449517SBill.Taylor@Sun.COM 		    (uint64_t)qp->qp_ring);
7459517SBill.Taylor@Sun.COM 
7469517SBill.Taylor@Sun.COM 		/* the FMA retry loop ends. */
7479517SBill.Taylor@Sun.COM 		hermon_pio_end(state, uarhdl, pio_error, fm_loop_cnt,
7489517SBill.Taylor@Sun.COM 		    fm_status, fm_test_num);
7499517SBill.Taylor@Sun.COM 
7509517SBill.Taylor@Sun.COM 		/* do the invalidate of the headroom */
7519517SBill.Taylor@Sun.COM 		wqe_start = (uint32_t *)HERMON_QP_SQ_ENTRY(qp,
7529517SBill.Taylor@Sun.COM 		    (tail + hdrmwqes) & qsize_msk);
7539517SBill.Taylor@Sun.COM 		for (i = 16; i < sectperwqe; i += 16) {
7549517SBill.Taylor@Sun.COM 			wqe_start[i] = 0xFFFFFFFF;
7559517SBill.Taylor@Sun.COM 		}
7569517SBill.Taylor@Sun.COM 	}
7579517SBill.Taylor@Sun.COM 	/*
7589517SBill.Taylor@Sun.COM 	 * Update the "num_posted" return value (if necessary).
7599517SBill.Taylor@Sun.COM 	 * Then drop the locks and return success.
7609517SBill.Taylor@Sun.COM 	 */
7619517SBill.Taylor@Sun.COM 	if (num_posted != NULL) {
7629517SBill.Taylor@Sun.COM 		*num_posted = posted_cnt;
7639517SBill.Taylor@Sun.COM 	}
7649517SBill.Taylor@Sun.COM 
7659517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_sq_lock);
7669517SBill.Taylor@Sun.COM 	return (status);
7679517SBill.Taylor@Sun.COM 
7689517SBill.Taylor@Sun.COM pio_error:
7699517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_sq_lock);
7709517SBill.Taylor@Sun.COM 	hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
7719517SBill.Taylor@Sun.COM 	return (ibc_get_ci_failure(0));
7729517SBill.Taylor@Sun.COM }
7739517SBill.Taylor@Sun.COM 
7749517SBill.Taylor@Sun.COM /*
7759517SBill.Taylor@Sun.COM  * hermon_post_send()
7769517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
7779517SBill.Taylor@Sun.COM  */
7789517SBill.Taylor@Sun.COM int
hermon_post_send(hermon_state_t * state,hermon_qphdl_t qp,ibt_send_wr_t * wr,uint_t num_wr,uint_t * num_posted)7799517SBill.Taylor@Sun.COM hermon_post_send(hermon_state_t *state, hermon_qphdl_t qp,
7809517SBill.Taylor@Sun.COM     ibt_send_wr_t *wr, uint_t num_wr, uint_t *num_posted)
7819517SBill.Taylor@Sun.COM {
7829517SBill.Taylor@Sun.COM 	ibt_send_wr_t 			*curr_wr;
7839517SBill.Taylor@Sun.COM 	hermon_workq_hdr_t		*wq;
7849517SBill.Taylor@Sun.COM 	hermon_ahhdl_t			ah;
7859517SBill.Taylor@Sun.COM 	uint64_t			*desc, *prev;
7869517SBill.Taylor@Sun.COM 	uint32_t			desc_sz;
7879517SBill.Taylor@Sun.COM 	uint32_t			signaled_dbd, solicited;
7889517SBill.Taylor@Sun.COM 	uint32_t			head, tail, next_tail, qsize_msk;
7899517SBill.Taylor@Sun.COM 	uint32_t			hdrmwqes;
7909517SBill.Taylor@Sun.COM 	uint_t				currindx, wrindx, numremain;
7919517SBill.Taylor@Sun.COM 	uint_t				chainlen;
7929517SBill.Taylor@Sun.COM 	uint_t				posted_cnt, maxstat;
7939517SBill.Taylor@Sun.COM 	uint_t				total_posted;
7949517SBill.Taylor@Sun.COM 	int				status;
7959517SBill.Taylor@Sun.COM 	uint32_t			nopcode, fence, immed_data = 0;
7969517SBill.Taylor@Sun.COM 	uint32_t			prev_nopcode;
797*12965SWilliam.Taylor@Oracle.COM 	uint_t				qp_state;
7989517SBill.Taylor@Sun.COM 
7999517SBill.Taylor@Sun.COM 	/* initialize the FMA retry loop */
8009517SBill.Taylor@Sun.COM 	hermon_pio_init(fm_loop_cnt, fm_status, fm_test);
8019517SBill.Taylor@Sun.COM 
8029517SBill.Taylor@Sun.COM 	/*
8039517SBill.Taylor@Sun.COM 	 * Check for user-mappable QP memory.  Note:  We do not allow kernel
8049517SBill.Taylor@Sun.COM 	 * clients to post to QP memory that is accessible directly by the
8059517SBill.Taylor@Sun.COM 	 * user.  If the QP memory is user accessible, then return an error.
8069517SBill.Taylor@Sun.COM 	 */
807*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_alloc_flags & IBT_QP_USER_MAP) {
8089517SBill.Taylor@Sun.COM 		return (IBT_QP_HDL_INVALID);
8099517SBill.Taylor@Sun.COM 	}
8109517SBill.Taylor@Sun.COM 
811*12965SWilliam.Taylor@Oracle.COM 	mutex_enter(&qp->qp_sq_lock);
8129517SBill.Taylor@Sun.COM 
8139517SBill.Taylor@Sun.COM 	/*
8149517SBill.Taylor@Sun.COM 	 * Check QP state.  Can not post Send requests from the "Reset",
8159517SBill.Taylor@Sun.COM 	 * "Init", or "RTR" states
8169517SBill.Taylor@Sun.COM 	 */
817*12965SWilliam.Taylor@Oracle.COM 	qp_state = qp->qp_state_for_post_send;
818*12965SWilliam.Taylor@Oracle.COM 	if ((qp_state == HERMON_QP_RESET) ||
819*12965SWilliam.Taylor@Oracle.COM 	    (qp_state == HERMON_QP_INIT) ||
820*12965SWilliam.Taylor@Oracle.COM 	    (qp_state == HERMON_QP_RTR)) {
821*12965SWilliam.Taylor@Oracle.COM 		mutex_exit(&qp->qp_sq_lock);
8229517SBill.Taylor@Sun.COM 		return (IBT_QP_STATE_INVALID);
8239517SBill.Taylor@Sun.COM 	}
8249517SBill.Taylor@Sun.COM 
8259517SBill.Taylor@Sun.COM 	if (qp->qp_is_special)
8269517SBill.Taylor@Sun.COM 		goto post_many;
8279517SBill.Taylor@Sun.COM 
8289517SBill.Taylor@Sun.COM 	/* Use these optimized functions most of the time */
829*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_type == IBT_UD_RQP) {
8309517SBill.Taylor@Sun.COM 		return (hermon_post_send_ud(state, qp, wr, num_wr, num_posted));
83111972SBill.Taylor@Sun.COM 	}
8329517SBill.Taylor@Sun.COM 
83311972SBill.Taylor@Sun.COM 	if (qp->qp_serv_type == HERMON_QP_RC) {
8349517SBill.Taylor@Sun.COM 		return (hermon_post_send_rc(state, qp, wr, num_wr, num_posted));
83511972SBill.Taylor@Sun.COM 	}
8369517SBill.Taylor@Sun.COM 
8379517SBill.Taylor@Sun.COM 	if (qp->qp_serv_type == HERMON_QP_UC)
8389517SBill.Taylor@Sun.COM 		goto post_many;
8399517SBill.Taylor@Sun.COM 
8409517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_sq_lock);
8419517SBill.Taylor@Sun.COM 	return (IBT_QP_SRV_TYPE_INVALID);
8429517SBill.Taylor@Sun.COM 
8439517SBill.Taylor@Sun.COM post_many:
8449517SBill.Taylor@Sun.COM 	/* general loop for non-optimized posting */
8459517SBill.Taylor@Sun.COM 
8469517SBill.Taylor@Sun.COM 	/* Save away some initial QP state */
8479517SBill.Taylor@Sun.COM 	wq = qp->qp_sq_wqhdr;
8489517SBill.Taylor@Sun.COM 	qsize_msk = wq->wq_mask;
8499517SBill.Taylor@Sun.COM 	tail	  = wq->wq_tail;
8509517SBill.Taylor@Sun.COM 	head	  = wq->wq_head;
8519517SBill.Taylor@Sun.COM 	hdrmwqes  = qp->qp_sq_hdrmwqes;		/* in WQEs  */
8529517SBill.Taylor@Sun.COM 
8539517SBill.Taylor@Sun.COM 	/* Initialize posted_cnt */
8549517SBill.Taylor@Sun.COM 	posted_cnt = 0;
8559517SBill.Taylor@Sun.COM 	total_posted = 0;
8569517SBill.Taylor@Sun.COM 
8579517SBill.Taylor@Sun.COM 	/*
8589517SBill.Taylor@Sun.COM 	 * For each ibt_send_wr_t in the wr[] list passed in, parse the
8599517SBill.Taylor@Sun.COM 	 * request and build a Send WQE.  NOTE:  Because we are potentially
8609517SBill.Taylor@Sun.COM 	 * building a chain of WQEs to post, we want to build them all first,
8619517SBill.Taylor@Sun.COM 	 * and set the valid (HW Ownership) bit on all but the first.
8629517SBill.Taylor@Sun.COM 	 * However, we do not want to validate the first one until the
8639517SBill.Taylor@Sun.COM 	 * entire chain of WQEs has been built.  Then in the final
8649517SBill.Taylor@Sun.COM 	 * we set the valid bit in the first, flush if needed, and as a last
8659517SBill.Taylor@Sun.COM 	 * step ring the appropriate doorbell.  NOTE: the doorbell ring may
8669517SBill.Taylor@Sun.COM 	 * NOT be needed if the HCA is already processing, but the doorbell
8679517SBill.Taylor@Sun.COM 	 * ring will be done regardless. NOTE ALSO:  It is possible for
8689517SBill.Taylor@Sun.COM 	 * more Work Requests to be posted than the HW will support at one
8699517SBill.Taylor@Sun.COM 	 * shot.  If this happens, we need to be able to post and ring
8709517SBill.Taylor@Sun.COM 	 * several chains here until the the entire request is complete.
8719517SBill.Taylor@Sun.COM 	 * NOTE ALSO:  the term "chain" is used to differentiate it from
8729517SBill.Taylor@Sun.COM 	 * Work Request List passed in; and because that's the terminology
8739517SBill.Taylor@Sun.COM 	 * from the previous generations of HCA - but the WQEs are not, in fact
8749517SBill.Taylor@Sun.COM 	 * chained together for Hermon
8759517SBill.Taylor@Sun.COM 	 */
8769517SBill.Taylor@Sun.COM 
8779517SBill.Taylor@Sun.COM 	wrindx = 0;
8789517SBill.Taylor@Sun.COM 	numremain = num_wr;
8799517SBill.Taylor@Sun.COM 	status	  = DDI_SUCCESS;
8809517SBill.Taylor@Sun.COM 	while ((wrindx < num_wr) && (status == DDI_SUCCESS)) {
8819517SBill.Taylor@Sun.COM 		/*
8829517SBill.Taylor@Sun.COM 		 * For the first WQE on a new chain we need "prev" to point
8839517SBill.Taylor@Sun.COM 		 * to the current descriptor.
8849517SBill.Taylor@Sun.COM 		 */
8859517SBill.Taylor@Sun.COM 		prev = HERMON_QP_SQ_ENTRY(qp, tail);
8869517SBill.Taylor@Sun.COM 
8879517SBill.Taylor@Sun.COM 		/*
8889517SBill.Taylor@Sun.COM 		 * Break the request up into lists that are less than or
8899517SBill.Taylor@Sun.COM 		 * equal to the maximum number of WQEs that can be posted
8909517SBill.Taylor@Sun.COM 		 * per doorbell ring - 256 currently
8919517SBill.Taylor@Sun.COM 		 */
8929517SBill.Taylor@Sun.COM 		chainlen = (numremain > HERMON_QP_MAXDESC_PER_DB) ?
8939517SBill.Taylor@Sun.COM 		    HERMON_QP_MAXDESC_PER_DB : numremain;
8949517SBill.Taylor@Sun.COM 		numremain -= chainlen;
8959517SBill.Taylor@Sun.COM 
8969517SBill.Taylor@Sun.COM 		for (currindx = 0; currindx < chainlen; currindx++, wrindx++) {
8979517SBill.Taylor@Sun.COM 			/*
8989517SBill.Taylor@Sun.COM 			 * Check for "queue full" condition.  If the queue
8999517SBill.Taylor@Sun.COM 			 * is already full, then no more WQEs can be posted.
9009517SBill.Taylor@Sun.COM 			 * So break out, ring a doorbell (if necessary) and
9019517SBill.Taylor@Sun.COM 			 * return an error
9029517SBill.Taylor@Sun.COM 			 */
9039517SBill.Taylor@Sun.COM 			if (wq->wq_full != 0) {
9049517SBill.Taylor@Sun.COM 				status = IBT_QP_FULL;
9059517SBill.Taylor@Sun.COM 				break;
9069517SBill.Taylor@Sun.COM 			}
9079517SBill.Taylor@Sun.COM 
9089517SBill.Taylor@Sun.COM 			/*
9099517SBill.Taylor@Sun.COM 			 * Increment the "tail index". Check for "queue
9109517SBill.Taylor@Sun.COM 			 * full" condition incl. headroom.  If we detect that
9119517SBill.Taylor@Sun.COM 			 * the current work request is going to fill the work
9129517SBill.Taylor@Sun.COM 			 * queue, then we mark this condition and continue.
9139517SBill.Taylor@Sun.COM 			 * Don't need >=, because going one-by-one we have to
9149517SBill.Taylor@Sun.COM 			 * hit it exactly sooner or later
9159517SBill.Taylor@Sun.COM 			 */
9169517SBill.Taylor@Sun.COM 
9179517SBill.Taylor@Sun.COM 			next_tail = (tail + 1) & qsize_msk;
9189517SBill.Taylor@Sun.COM 			if (((tail + hdrmwqes) & qsize_msk) == head) {
9199517SBill.Taylor@Sun.COM 				wq->wq_full = 1;
9209517SBill.Taylor@Sun.COM 			}
9219517SBill.Taylor@Sun.COM 
9229517SBill.Taylor@Sun.COM 			/*
9239517SBill.Taylor@Sun.COM 			 * Get the address of the location where the next
9249517SBill.Taylor@Sun.COM 			 * Send WQE should be built
9259517SBill.Taylor@Sun.COM 			 */
9269517SBill.Taylor@Sun.COM 			desc = HERMON_QP_SQ_ENTRY(qp, tail);
9279517SBill.Taylor@Sun.COM 			/*
9289517SBill.Taylor@Sun.COM 			 * Call hermon_wqe_send_build() to build the WQE
9299517SBill.Taylor@Sun.COM 			 * at the given address.  This routine uses the
9309517SBill.Taylor@Sun.COM 			 * information in the ibt_send_wr_t list (wr[]) and
9319517SBill.Taylor@Sun.COM 			 * returns the size of the WQE when it returns.
9329517SBill.Taylor@Sun.COM 			 */
9339517SBill.Taylor@Sun.COM 			status = hermon_wqe_send_build(state, qp,
9349517SBill.Taylor@Sun.COM 			    &wr[wrindx], desc, &desc_sz);
9359517SBill.Taylor@Sun.COM 			if (status != DDI_SUCCESS) {
9369517SBill.Taylor@Sun.COM 				break;
9379517SBill.Taylor@Sun.COM 			}
9389517SBill.Taylor@Sun.COM 
9399517SBill.Taylor@Sun.COM 			/*
9409517SBill.Taylor@Sun.COM 			 * Now, build the Ctrl Segment based on
9419517SBill.Taylor@Sun.COM 			 * what was just done
9429517SBill.Taylor@Sun.COM 			 */
9439517SBill.Taylor@Sun.COM 			curr_wr = &wr[wrindx];
9449517SBill.Taylor@Sun.COM 
9459517SBill.Taylor@Sun.COM 			switch (curr_wr->wr_opcode) {
9469517SBill.Taylor@Sun.COM 			case IBT_WRC_RDMAW:
9479517SBill.Taylor@Sun.COM 				if (curr_wr->wr_flags & IBT_WR_SEND_IMMED) {
9489517SBill.Taylor@Sun.COM 					nopcode =
9499517SBill.Taylor@Sun.COM 					    HERMON_WQE_SEND_NOPCODE_RDMAWI;
9509517SBill.Taylor@Sun.COM 					immed_data =
9519517SBill.Taylor@Sun.COM 					    hermon_wr_get_immediate(curr_wr);
9529517SBill.Taylor@Sun.COM 				} else {
9539517SBill.Taylor@Sun.COM 					nopcode = HERMON_WQE_SEND_NOPCODE_RDMAW;
9549517SBill.Taylor@Sun.COM 				}
9559517SBill.Taylor@Sun.COM 				break;
9569517SBill.Taylor@Sun.COM 
9579517SBill.Taylor@Sun.COM 			case IBT_WRC_SEND:
9589517SBill.Taylor@Sun.COM 				if (curr_wr->wr_flags & IBT_WR_SEND_IMMED) {
9599517SBill.Taylor@Sun.COM 					nopcode = HERMON_WQE_SEND_NOPCODE_SENDI;
9609517SBill.Taylor@Sun.COM 					immed_data =
9619517SBill.Taylor@Sun.COM 					    hermon_wr_get_immediate(curr_wr);
9629517SBill.Taylor@Sun.COM 				} else {
9639517SBill.Taylor@Sun.COM 					nopcode = HERMON_WQE_SEND_NOPCODE_SEND;
9649517SBill.Taylor@Sun.COM 				}
9659517SBill.Taylor@Sun.COM 				break;
9669517SBill.Taylor@Sun.COM 
9679517SBill.Taylor@Sun.COM 			case IBT_WRC_SEND_LSO:
9689517SBill.Taylor@Sun.COM 				nopcode = HERMON_WQE_SEND_NOPCODE_LSO;
9699517SBill.Taylor@Sun.COM 				break;
9709517SBill.Taylor@Sun.COM 
9719517SBill.Taylor@Sun.COM 			case IBT_WRC_RDMAR:
9729517SBill.Taylor@Sun.COM 				nopcode = HERMON_WQE_SEND_NOPCODE_RDMAR;
9739517SBill.Taylor@Sun.COM 				break;
9749517SBill.Taylor@Sun.COM 
9759517SBill.Taylor@Sun.COM 			case IBT_WRC_CSWAP:
9769517SBill.Taylor@Sun.COM 				nopcode = HERMON_WQE_SEND_NOPCODE_ATMCS;
9779517SBill.Taylor@Sun.COM 				break;
9789517SBill.Taylor@Sun.COM 
9799517SBill.Taylor@Sun.COM 			case IBT_WRC_FADD:
9809517SBill.Taylor@Sun.COM 				nopcode = HERMON_WQE_SEND_NOPCODE_ATMFA;
9819517SBill.Taylor@Sun.COM 				break;
9829517SBill.Taylor@Sun.COM 
9839517SBill.Taylor@Sun.COM 			case IBT_WRC_BIND:
9849517SBill.Taylor@Sun.COM 				nopcode = HERMON_WQE_SEND_NOPCODE_BIND;
9859517SBill.Taylor@Sun.COM 				break;
9869517SBill.Taylor@Sun.COM 			}
9879517SBill.Taylor@Sun.COM 
9889517SBill.Taylor@Sun.COM 			fence = (curr_wr->wr_flags & IBT_WR_SEND_FENCE) ? 1 : 0;
9899517SBill.Taylor@Sun.COM 
9909517SBill.Taylor@Sun.COM 			/*
9919517SBill.Taylor@Sun.COM 			 * now, build up the control segment, leaving the
9929517SBill.Taylor@Sun.COM 			 * owner bit as it is
9939517SBill.Taylor@Sun.COM 			 */
9949517SBill.Taylor@Sun.COM 
9959517SBill.Taylor@Sun.COM 			if ((qp->qp_sq_sigtype == HERMON_QP_SQ_ALL_SIGNALED) ||
9969517SBill.Taylor@Sun.COM 			    (curr_wr->wr_flags & IBT_WR_SEND_SIGNAL)) {
997*12965SWilliam.Taylor@Oracle.COM 				signaled_dbd = 0xC;
9989517SBill.Taylor@Sun.COM 			} else {
9999517SBill.Taylor@Sun.COM 				signaled_dbd = 0;
10009517SBill.Taylor@Sun.COM 			}
10019517SBill.Taylor@Sun.COM 			if (curr_wr->wr_flags & IBT_WR_SEND_SOLICIT)
1002*12965SWilliam.Taylor@Oracle.COM 				solicited = 0x2;
10039517SBill.Taylor@Sun.COM 			else
10049517SBill.Taylor@Sun.COM 				solicited = 0;
10059517SBill.Taylor@Sun.COM 
10069517SBill.Taylor@Sun.COM 			if (qp->qp_is_special) {
100711643SBill.Taylor@Sun.COM 				/* Ensure correctness, set the ReRead bit */
100811643SBill.Taylor@Sun.COM 				nopcode |= (1 << 6);
10099517SBill.Taylor@Sun.COM 				ah = (hermon_ahhdl_t)
10109517SBill.Taylor@Sun.COM 				    curr_wr->wr.ud.udwr_dest->ud_ah;
10119517SBill.Taylor@Sun.COM 				mutex_enter(&ah->ah_lock);
10129517SBill.Taylor@Sun.COM 				maxstat = ah->ah_udav->max_stat_rate;
10139517SBill.Taylor@Sun.COM 				HERMON_WQE_SET_MLX_CTRL_SEGMENT(desc, desc_sz,
10149517SBill.Taylor@Sun.COM 				    signaled_dbd, maxstat, ah->ah_udav->rlid,
10159517SBill.Taylor@Sun.COM 				    qp, ah->ah_udav->sl);
10169517SBill.Taylor@Sun.COM 				mutex_exit(&ah->ah_lock);
10179517SBill.Taylor@Sun.COM 			} else {
10189517SBill.Taylor@Sun.COM 				HERMON_WQE_SET_CTRL_SEGMENT(desc, desc_sz,
10199517SBill.Taylor@Sun.COM 				    fence, immed_data, solicited,
1020*12965SWilliam.Taylor@Oracle.COM 				    signaled_dbd, 0, qp, 0, 0);
10219517SBill.Taylor@Sun.COM 			}
10229517SBill.Taylor@Sun.COM 			wq->wq_wrid[tail] = curr_wr->wr_id;
10239517SBill.Taylor@Sun.COM 
10249517SBill.Taylor@Sun.COM 			/*
10259517SBill.Taylor@Sun.COM 			 * If this is not the first descriptor on the current
10269517SBill.Taylor@Sun.COM 			 * chain, then set the ownership bit.
10279517SBill.Taylor@Sun.COM 			 */
10289517SBill.Taylor@Sun.COM 			if (currindx != 0) {		/* not the first */
10299517SBill.Taylor@Sun.COM 				membar_producer();
10309517SBill.Taylor@Sun.COM 				HERMON_SET_SEND_WQE_OWNER(qp,
10319517SBill.Taylor@Sun.COM 				    (uint32_t *)desc, nopcode);
10329517SBill.Taylor@Sun.COM 			} else
10339517SBill.Taylor@Sun.COM 				prev_nopcode = nopcode;
10349517SBill.Taylor@Sun.COM 
10359517SBill.Taylor@Sun.COM 			/*
10369517SBill.Taylor@Sun.COM 			 * Update the current "tail index" and increment
10379517SBill.Taylor@Sun.COM 			 * "posted_cnt"
10389517SBill.Taylor@Sun.COM 			 */
10399517SBill.Taylor@Sun.COM 			tail = next_tail;
10409517SBill.Taylor@Sun.COM 			posted_cnt++;
10419517SBill.Taylor@Sun.COM 		}
10429517SBill.Taylor@Sun.COM 
10439517SBill.Taylor@Sun.COM 		/*
10449517SBill.Taylor@Sun.COM 		 * If we reach here and there are one or more WQEs which have
10459517SBill.Taylor@Sun.COM 		 * been successfully built as a chain, we have to finish up
10469517SBill.Taylor@Sun.COM 		 * and prepare them for writing to the HW
10479517SBill.Taylor@Sun.COM 		 * The steps are:
10489517SBill.Taylor@Sun.COM 		 * 	1. do the headroom fixup
10499517SBill.Taylor@Sun.COM 		 *	2. add in the size of the headroom for the sync
10509517SBill.Taylor@Sun.COM 		 *	3. write the owner bit for the first WQE
10519517SBill.Taylor@Sun.COM 		 *	4. sync them
10529517SBill.Taylor@Sun.COM 		 *	5. fix up the structures
10539517SBill.Taylor@Sun.COM 		 *	6. hit the doorbell in UAR
10549517SBill.Taylor@Sun.COM 		 */
10559517SBill.Taylor@Sun.COM 		if (posted_cnt != 0) {
10569517SBill.Taylor@Sun.COM 			ddi_acc_handle_t uarhdl = hermon_get_uarhdl(state);
10579517SBill.Taylor@Sun.COM 
10589517SBill.Taylor@Sun.COM 			/* do the invalidate of the headroom */
10599517SBill.Taylor@Sun.COM 
10609517SBill.Taylor@Sun.COM 			hermon_wqe_headroom(tail, qp);
10619517SBill.Taylor@Sun.COM 
10629517SBill.Taylor@Sun.COM 			/* Update some of the state in the QP */
10639517SBill.Taylor@Sun.COM 			wq->wq_tail = tail;
10649517SBill.Taylor@Sun.COM 			total_posted += posted_cnt;
10659517SBill.Taylor@Sun.COM 			posted_cnt = 0;
10669517SBill.Taylor@Sun.COM 
10679517SBill.Taylor@Sun.COM 			membar_producer();
10689517SBill.Taylor@Sun.COM 
10699517SBill.Taylor@Sun.COM 			/*
10709517SBill.Taylor@Sun.COM 			 * Now set the ownership bit of the first
10719517SBill.Taylor@Sun.COM 			 * one in the chain
10729517SBill.Taylor@Sun.COM 			 */
10739517SBill.Taylor@Sun.COM 			HERMON_SET_SEND_WQE_OWNER(qp, (uint32_t *)prev,
10749517SBill.Taylor@Sun.COM 			    prev_nopcode);
10759517SBill.Taylor@Sun.COM 
10769517SBill.Taylor@Sun.COM 			/* the FMA retry loop starts for Hermon doorbell. */
10779517SBill.Taylor@Sun.COM 			hermon_pio_start(state, uarhdl, pio_error, fm_loop_cnt,
10789517SBill.Taylor@Sun.COM 			    fm_status, fm_test);
10799517SBill.Taylor@Sun.COM 
10809517SBill.Taylor@Sun.COM 			HERMON_UAR_DOORBELL(state, uarhdl,
10819517SBill.Taylor@Sun.COM 			    (uint64_t *)(void *)&state->hs_uar->send,
10829517SBill.Taylor@Sun.COM 			    (uint64_t)qp->qp_ring);
10839517SBill.Taylor@Sun.COM 
10849517SBill.Taylor@Sun.COM 			/* the FMA retry loop ends. */
10859517SBill.Taylor@Sun.COM 			hermon_pio_end(state, uarhdl, pio_error, fm_loop_cnt,
10869517SBill.Taylor@Sun.COM 			    fm_status, fm_test);
10879517SBill.Taylor@Sun.COM 		}
10889517SBill.Taylor@Sun.COM 	}
10899517SBill.Taylor@Sun.COM 
10909517SBill.Taylor@Sun.COM 	/*
10919517SBill.Taylor@Sun.COM 	 * Update the "num_posted" return value (if necessary).
10929517SBill.Taylor@Sun.COM 	 * Then drop the locks and return success.
10939517SBill.Taylor@Sun.COM 	 */
10949517SBill.Taylor@Sun.COM 	if (num_posted != NULL) {
10959517SBill.Taylor@Sun.COM 		*num_posted = total_posted;
10969517SBill.Taylor@Sun.COM 	}
10979517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_sq_lock);
10989517SBill.Taylor@Sun.COM 	return (status);
10999517SBill.Taylor@Sun.COM 
11009517SBill.Taylor@Sun.COM pio_error:
11019517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_sq_lock);
11029517SBill.Taylor@Sun.COM 	hermon_fm_ereport(state, HCA_SYS_ERR, HCA_ERR_SRV_LOST);
11039517SBill.Taylor@Sun.COM 	return (ibc_get_ci_failure(0));
11049517SBill.Taylor@Sun.COM }
11059517SBill.Taylor@Sun.COM 
11069517SBill.Taylor@Sun.COM 
11079517SBill.Taylor@Sun.COM /*
11089517SBill.Taylor@Sun.COM  * hermon_post_recv()
11099517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
11109517SBill.Taylor@Sun.COM  */
11119517SBill.Taylor@Sun.COM int
hermon_post_recv(hermon_state_t * state,hermon_qphdl_t qp,ibt_recv_wr_t * wr,uint_t num_wr,uint_t * num_posted)11129517SBill.Taylor@Sun.COM hermon_post_recv(hermon_state_t *state, hermon_qphdl_t qp,
11139517SBill.Taylor@Sun.COM     ibt_recv_wr_t *wr, uint_t num_wr, uint_t *num_posted)
11149517SBill.Taylor@Sun.COM {
11159517SBill.Taylor@Sun.COM 	uint64_t			*desc;
11169517SBill.Taylor@Sun.COM 	hermon_workq_hdr_t		*wq;
11179517SBill.Taylor@Sun.COM 	uint32_t			head, tail, next_tail, qsize_msk;
11189517SBill.Taylor@Sun.COM 	uint_t				wrindx;
11199517SBill.Taylor@Sun.COM 	uint_t				posted_cnt;
11209517SBill.Taylor@Sun.COM 	int				status;
11219517SBill.Taylor@Sun.COM 
11229517SBill.Taylor@Sun.COM 	/*
11239517SBill.Taylor@Sun.COM 	 * Check for user-mappable QP memory.  Note:  We do not allow kernel
11249517SBill.Taylor@Sun.COM 	 * clients to post to QP memory that is accessible directly by the
11259517SBill.Taylor@Sun.COM 	 * user.  If the QP memory is user accessible, then return an error.
11269517SBill.Taylor@Sun.COM 	 */
1127*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_alloc_flags & IBT_QP_USER_MAP) {
11289517SBill.Taylor@Sun.COM 		return (IBT_QP_HDL_INVALID);
11299517SBill.Taylor@Sun.COM 	}
11309517SBill.Taylor@Sun.COM 
11319517SBill.Taylor@Sun.COM 	/* Initialize posted_cnt */
11329517SBill.Taylor@Sun.COM 	posted_cnt = 0;
11339517SBill.Taylor@Sun.COM 
11349517SBill.Taylor@Sun.COM 	mutex_enter(&qp->qp_lock);
11359517SBill.Taylor@Sun.COM 
11369517SBill.Taylor@Sun.COM 	/*
11379517SBill.Taylor@Sun.COM 	 * Check if QP is associated with an SRQ
11389517SBill.Taylor@Sun.COM 	 */
1139*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_alloc_flags & IBT_QP_USES_SRQ) {
11409517SBill.Taylor@Sun.COM 		mutex_exit(&qp->qp_lock);
11419517SBill.Taylor@Sun.COM 		return (IBT_SRQ_IN_USE);
11429517SBill.Taylor@Sun.COM 	}
11439517SBill.Taylor@Sun.COM 
11449517SBill.Taylor@Sun.COM 	/*
11459517SBill.Taylor@Sun.COM 	 * Check QP state.  Can not post Recv requests from the "Reset" state
11469517SBill.Taylor@Sun.COM 	 */
11479517SBill.Taylor@Sun.COM 	if (qp->qp_state == HERMON_QP_RESET) {
11489517SBill.Taylor@Sun.COM 		mutex_exit(&qp->qp_lock);
11499517SBill.Taylor@Sun.COM 		return (IBT_QP_STATE_INVALID);
11509517SBill.Taylor@Sun.COM 	}
11519517SBill.Taylor@Sun.COM 
11529517SBill.Taylor@Sun.COM 	/* Check that work request transport type is valid */
1153*12965SWilliam.Taylor@Oracle.COM 	if ((qp->qp_type != IBT_UD_RQP) &&
11549517SBill.Taylor@Sun.COM 	    (qp->qp_serv_type != HERMON_QP_RC) &&
11559517SBill.Taylor@Sun.COM 	    (qp->qp_serv_type != HERMON_QP_UC)) {
11569517SBill.Taylor@Sun.COM 		mutex_exit(&qp->qp_lock);
11579517SBill.Taylor@Sun.COM 		return (IBT_QP_SRV_TYPE_INVALID);
11589517SBill.Taylor@Sun.COM 	}
11599517SBill.Taylor@Sun.COM 
11609517SBill.Taylor@Sun.COM 	/*
11619517SBill.Taylor@Sun.COM 	 * Grab the lock for the WRID list, i.e., membar_consumer().
11629517SBill.Taylor@Sun.COM 	 * This is not needed because the mutex_enter() above has
11639517SBill.Taylor@Sun.COM 	 * the same effect.
11649517SBill.Taylor@Sun.COM 	 */
11659517SBill.Taylor@Sun.COM 
11669517SBill.Taylor@Sun.COM 	/* Save away some initial QP state */
11679517SBill.Taylor@Sun.COM 	wq = qp->qp_rq_wqhdr;
11689517SBill.Taylor@Sun.COM 	qsize_msk = wq->wq_mask;
11699517SBill.Taylor@Sun.COM 	tail	  = wq->wq_tail;
11709517SBill.Taylor@Sun.COM 	head	  = wq->wq_head;
11719517SBill.Taylor@Sun.COM 
11729517SBill.Taylor@Sun.COM 	wrindx = 0;
11739517SBill.Taylor@Sun.COM 	status	  = DDI_SUCCESS;
11749517SBill.Taylor@Sun.COM 
11759517SBill.Taylor@Sun.COM 	for (wrindx = 0; wrindx < num_wr; wrindx++) {
11769517SBill.Taylor@Sun.COM 		if (wq->wq_full != 0) {
11779517SBill.Taylor@Sun.COM 			status = IBT_QP_FULL;
11789517SBill.Taylor@Sun.COM 			break;
11799517SBill.Taylor@Sun.COM 		}
11809517SBill.Taylor@Sun.COM 		next_tail = (tail + 1) & qsize_msk;
11819517SBill.Taylor@Sun.COM 		if (next_tail == head) {
11829517SBill.Taylor@Sun.COM 			wq->wq_full = 1;
11839517SBill.Taylor@Sun.COM 		}
11849517SBill.Taylor@Sun.COM 		desc = HERMON_QP_RQ_ENTRY(qp, tail);
11859517SBill.Taylor@Sun.COM 		status = hermon_wqe_recv_build(state, qp, &wr[wrindx], desc);
11869517SBill.Taylor@Sun.COM 		if (status != DDI_SUCCESS) {
11879517SBill.Taylor@Sun.COM 			break;
11889517SBill.Taylor@Sun.COM 		}
11899517SBill.Taylor@Sun.COM 
11909517SBill.Taylor@Sun.COM 		wq->wq_wrid[tail] = wr[wrindx].wr_id;
11919517SBill.Taylor@Sun.COM 		qp->qp_rq_wqecntr++;
11929517SBill.Taylor@Sun.COM 
11939517SBill.Taylor@Sun.COM 		tail = next_tail;
11949517SBill.Taylor@Sun.COM 		posted_cnt++;
11959517SBill.Taylor@Sun.COM 	}
11969517SBill.Taylor@Sun.COM 
11979517SBill.Taylor@Sun.COM 	if (posted_cnt != 0) {
11989517SBill.Taylor@Sun.COM 
11999517SBill.Taylor@Sun.COM 		wq->wq_tail = tail;
12009517SBill.Taylor@Sun.COM 
12019517SBill.Taylor@Sun.COM 		membar_producer();	/* ensure wrids are visible */
12029517SBill.Taylor@Sun.COM 
12039517SBill.Taylor@Sun.COM 		/* Update the doorbell record w/ wqecntr */
12049517SBill.Taylor@Sun.COM 		HERMON_UAR_DB_RECORD_WRITE(qp->qp_rq_vdbr,
12059517SBill.Taylor@Sun.COM 		    qp->qp_rq_wqecntr & 0xFFFF);
12069517SBill.Taylor@Sun.COM 	}
12079517SBill.Taylor@Sun.COM 
12089517SBill.Taylor@Sun.COM 	if (num_posted != NULL) {
12099517SBill.Taylor@Sun.COM 		*num_posted = posted_cnt;
12109517SBill.Taylor@Sun.COM 	}
12119517SBill.Taylor@Sun.COM 
12129517SBill.Taylor@Sun.COM 
1213*12965SWilliam.Taylor@Oracle.COM 	mutex_exit(&qp->qp_lock);
12149517SBill.Taylor@Sun.COM 	return (status);
12159517SBill.Taylor@Sun.COM }
12169517SBill.Taylor@Sun.COM 
12179517SBill.Taylor@Sun.COM /*
12189517SBill.Taylor@Sun.COM  * hermon_post_srq()
12199517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
12209517SBill.Taylor@Sun.COM  */
12219517SBill.Taylor@Sun.COM int
hermon_post_srq(hermon_state_t * state,hermon_srqhdl_t srq,ibt_recv_wr_t * wr,uint_t num_wr,uint_t * num_posted)12229517SBill.Taylor@Sun.COM hermon_post_srq(hermon_state_t *state, hermon_srqhdl_t srq,
12239517SBill.Taylor@Sun.COM     ibt_recv_wr_t *wr, uint_t num_wr, uint_t *num_posted)
12249517SBill.Taylor@Sun.COM {
12259517SBill.Taylor@Sun.COM 	uint64_t			*desc;
12269517SBill.Taylor@Sun.COM 	hermon_workq_hdr_t		*wq;
12279517SBill.Taylor@Sun.COM 	uint_t				indx, wrindx;
12289517SBill.Taylor@Sun.COM 	uint_t				posted_cnt;
12299517SBill.Taylor@Sun.COM 	int				status;
12309517SBill.Taylor@Sun.COM 
12319517SBill.Taylor@Sun.COM 	mutex_enter(&srq->srq_lock);
12329517SBill.Taylor@Sun.COM 
12339517SBill.Taylor@Sun.COM 	/*
12349517SBill.Taylor@Sun.COM 	 * Check for user-mappable QP memory.  Note:  We do not allow kernel
12359517SBill.Taylor@Sun.COM 	 * clients to post to QP memory that is accessible directly by the
12369517SBill.Taylor@Sun.COM 	 * user.  If the QP memory is user accessible, then return an error.
12379517SBill.Taylor@Sun.COM 	 */
12389517SBill.Taylor@Sun.COM 	if (srq->srq_is_umap) {
12399517SBill.Taylor@Sun.COM 		mutex_exit(&srq->srq_lock);
12409517SBill.Taylor@Sun.COM 		return (IBT_SRQ_HDL_INVALID);
12419517SBill.Taylor@Sun.COM 	}
12429517SBill.Taylor@Sun.COM 
12439517SBill.Taylor@Sun.COM 	/*
12449517SBill.Taylor@Sun.COM 	 * Check SRQ state.  Can not post Recv requests when SRQ is in error
12459517SBill.Taylor@Sun.COM 	 */
12469517SBill.Taylor@Sun.COM 	if (srq->srq_state == HERMON_SRQ_STATE_ERROR) {
12479517SBill.Taylor@Sun.COM 		mutex_exit(&srq->srq_lock);
12489517SBill.Taylor@Sun.COM 		return (IBT_QP_STATE_INVALID);
12499517SBill.Taylor@Sun.COM 	}
12509517SBill.Taylor@Sun.COM 
12519517SBill.Taylor@Sun.COM 	status = DDI_SUCCESS;
12529517SBill.Taylor@Sun.COM 	posted_cnt = 0;
12539517SBill.Taylor@Sun.COM 	wq = srq->srq_wq_wqhdr;
12549517SBill.Taylor@Sun.COM 	indx = wq->wq_head;
12559517SBill.Taylor@Sun.COM 
12569517SBill.Taylor@Sun.COM 	for (wrindx = 0; wrindx < num_wr; wrindx++) {
12579517SBill.Taylor@Sun.COM 
12589517SBill.Taylor@Sun.COM 		if (indx == wq->wq_tail) {
12599517SBill.Taylor@Sun.COM 			status = IBT_QP_FULL;
12609517SBill.Taylor@Sun.COM 			break;
12619517SBill.Taylor@Sun.COM 		}
12629517SBill.Taylor@Sun.COM 		desc = HERMON_SRQ_WQE_ADDR(srq, indx);
12639517SBill.Taylor@Sun.COM 
12649517SBill.Taylor@Sun.COM 		wq->wq_wrid[indx] = wr[wrindx].wr_id;
12659517SBill.Taylor@Sun.COM 
12669517SBill.Taylor@Sun.COM 		status = hermon_wqe_srq_build(state, srq, &wr[wrindx], desc);
12679517SBill.Taylor@Sun.COM 		if (status != DDI_SUCCESS) {
12689517SBill.Taylor@Sun.COM 			break;
12699517SBill.Taylor@Sun.COM 		}
12709517SBill.Taylor@Sun.COM 
12719517SBill.Taylor@Sun.COM 		posted_cnt++;
12729517SBill.Taylor@Sun.COM 		indx = htons(((uint16_t *)desc)[1]);
12739517SBill.Taylor@Sun.COM 		wq->wq_head = indx;
12749517SBill.Taylor@Sun.COM 	}
12759517SBill.Taylor@Sun.COM 
12769517SBill.Taylor@Sun.COM 	if (posted_cnt != 0) {
12779517SBill.Taylor@Sun.COM 
12789517SBill.Taylor@Sun.COM 		srq->srq_wq_wqecntr += posted_cnt;
12799517SBill.Taylor@Sun.COM 
12809517SBill.Taylor@Sun.COM 		membar_producer();	/* ensure wrids are visible */
12819517SBill.Taylor@Sun.COM 
12829517SBill.Taylor@Sun.COM 		/* Ring the doorbell w/ wqecntr */
12839517SBill.Taylor@Sun.COM 		HERMON_UAR_DB_RECORD_WRITE(srq->srq_wq_vdbr,
12849517SBill.Taylor@Sun.COM 		    srq->srq_wq_wqecntr & 0xFFFF);
12859517SBill.Taylor@Sun.COM 	}
12869517SBill.Taylor@Sun.COM 
12879517SBill.Taylor@Sun.COM 	if (num_posted != NULL) {
12889517SBill.Taylor@Sun.COM 		*num_posted = posted_cnt;
12899517SBill.Taylor@Sun.COM 	}
12909517SBill.Taylor@Sun.COM 
12919517SBill.Taylor@Sun.COM 	mutex_exit(&srq->srq_lock);
12929517SBill.Taylor@Sun.COM 	return (status);
12939517SBill.Taylor@Sun.COM }
12949517SBill.Taylor@Sun.COM 
12959517SBill.Taylor@Sun.COM 
12969517SBill.Taylor@Sun.COM /*
12979517SBill.Taylor@Sun.COM  * hermon_wqe_send_build()
12989517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
12999517SBill.Taylor@Sun.COM  */
13009517SBill.Taylor@Sun.COM static int
hermon_wqe_send_build(hermon_state_t * state,hermon_qphdl_t qp,ibt_send_wr_t * wr,uint64_t * desc,uint_t * size)13019517SBill.Taylor@Sun.COM hermon_wqe_send_build(hermon_state_t *state, hermon_qphdl_t qp,
13029517SBill.Taylor@Sun.COM     ibt_send_wr_t *wr, uint64_t *desc, uint_t *size)
13039517SBill.Taylor@Sun.COM {
13049517SBill.Taylor@Sun.COM 	hermon_hw_snd_wqe_ud_t		*ud;
13059517SBill.Taylor@Sun.COM 	hermon_hw_snd_wqe_remaddr_t	*rc;
13069517SBill.Taylor@Sun.COM 	hermon_hw_snd_wqe_atomic_t	*at;
13079517SBill.Taylor@Sun.COM 	hermon_hw_snd_wqe_remaddr_t	*uc;
13089517SBill.Taylor@Sun.COM 	hermon_hw_snd_wqe_bind_t	*bn;
13099517SBill.Taylor@Sun.COM 	hermon_hw_wqe_sgl_t		*ds, *old_ds;
13109517SBill.Taylor@Sun.COM 	ibt_ud_dest_t			*dest;
13119517SBill.Taylor@Sun.COM 	ibt_wr_ds_t			*sgl;
13129517SBill.Taylor@Sun.COM 	hermon_ahhdl_t			ah;
13139517SBill.Taylor@Sun.COM 	uint32_t			nds;
13149517SBill.Taylor@Sun.COM 	int				i, j, last_ds, num_ds, status;
13159517SBill.Taylor@Sun.COM 	int				tmpsize;
13169517SBill.Taylor@Sun.COM 
13179517SBill.Taylor@Sun.COM 	ASSERT(MUTEX_HELD(&qp->qp_sq_lock));
13189517SBill.Taylor@Sun.COM 
13199517SBill.Taylor@Sun.COM 	/* Initialize the information for the Data Segments */
13209517SBill.Taylor@Sun.COM 	ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)desc +
13219517SBill.Taylor@Sun.COM 	    sizeof (hermon_hw_snd_wqe_ctrl_t));
13229517SBill.Taylor@Sun.COM 	nds = wr->wr_nds;
13239517SBill.Taylor@Sun.COM 	sgl = wr->wr_sgl;
13249517SBill.Taylor@Sun.COM 	num_ds = 0;
13259517SBill.Taylor@Sun.COM 	i = 0;
13269517SBill.Taylor@Sun.COM 
13279517SBill.Taylor@Sun.COM 	/*
13289517SBill.Taylor@Sun.COM 	 * Build a Send WQE depends first and foremost on the transport
13299517SBill.Taylor@Sun.COM 	 * type of Work Request (i.e. UD, RC, or UC)
13309517SBill.Taylor@Sun.COM 	 */
13319517SBill.Taylor@Sun.COM 	switch (wr->wr_trans) {
13329517SBill.Taylor@Sun.COM 	case IBT_UD_SRV:
13339517SBill.Taylor@Sun.COM 		/* Ensure that work request transport type matches QP type */
13349517SBill.Taylor@Sun.COM 		if (qp->qp_serv_type != HERMON_QP_UD) {
13359517SBill.Taylor@Sun.COM 			return (IBT_QP_SRV_TYPE_INVALID);
13369517SBill.Taylor@Sun.COM 		}
13379517SBill.Taylor@Sun.COM 
13389517SBill.Taylor@Sun.COM 		/*
13399517SBill.Taylor@Sun.COM 		 * Validate the operation type.  For UD requests, only the
13409517SBill.Taylor@Sun.COM 		 * "Send" and "Send LSO" operations are valid.
13419517SBill.Taylor@Sun.COM 		 */
13429517SBill.Taylor@Sun.COM 		if (wr->wr_opcode != IBT_WRC_SEND &&
13439517SBill.Taylor@Sun.COM 		    wr->wr_opcode != IBT_WRC_SEND_LSO) {
13449517SBill.Taylor@Sun.COM 			return (IBT_QP_OP_TYPE_INVALID);
13459517SBill.Taylor@Sun.COM 		}
13469517SBill.Taylor@Sun.COM 
13479517SBill.Taylor@Sun.COM 		/*
13489517SBill.Taylor@Sun.COM 		 * If this is a Special QP (QP0 or QP1), then we need to
13499517SBill.Taylor@Sun.COM 		 * build MLX WQEs instead.  So jump to hermon_wqe_mlx_build()
13509517SBill.Taylor@Sun.COM 		 * and return whatever status it returns
13519517SBill.Taylor@Sun.COM 		 */
13529517SBill.Taylor@Sun.COM 		if (qp->qp_is_special) {
13539517SBill.Taylor@Sun.COM 			if (wr->wr_opcode == IBT_WRC_SEND_LSO) {
13549517SBill.Taylor@Sun.COM 				return (IBT_QP_OP_TYPE_INVALID);
13559517SBill.Taylor@Sun.COM 			}
13569517SBill.Taylor@Sun.COM 			status = hermon_wqe_mlx_build(state, qp,
13579517SBill.Taylor@Sun.COM 			    wr, desc, size);
13589517SBill.Taylor@Sun.COM 			return (status);
13599517SBill.Taylor@Sun.COM 		}
13609517SBill.Taylor@Sun.COM 
13619517SBill.Taylor@Sun.COM 		/*
13629517SBill.Taylor@Sun.COM 		 * Otherwise, if this is a normal UD Send request, then fill
13639517SBill.Taylor@Sun.COM 		 * all the fields in the Hermon UD header for the WQE.  Note:
13649517SBill.Taylor@Sun.COM 		 * to do this we'll need to extract some information from the
13659517SBill.Taylor@Sun.COM 		 * Address Handle passed with the work request.
13669517SBill.Taylor@Sun.COM 		 */
13679517SBill.Taylor@Sun.COM 		ud = (hermon_hw_snd_wqe_ud_t *)((uintptr_t)desc +
13689517SBill.Taylor@Sun.COM 		    sizeof (hermon_hw_snd_wqe_ctrl_t));
13699517SBill.Taylor@Sun.COM 		if (wr->wr_opcode == IBT_WRC_SEND) {
13709517SBill.Taylor@Sun.COM 			dest = wr->wr.ud.udwr_dest;
13719517SBill.Taylor@Sun.COM 		} else {
13729517SBill.Taylor@Sun.COM 			dest = wr->wr.ud_lso.lso_ud_dest;
13739517SBill.Taylor@Sun.COM 		}
13749517SBill.Taylor@Sun.COM 		ah = (hermon_ahhdl_t)dest->ud_ah;
13759517SBill.Taylor@Sun.COM 		if (ah == NULL) {
13769517SBill.Taylor@Sun.COM 			return (IBT_AH_HDL_INVALID);
13779517SBill.Taylor@Sun.COM 		}
13789517SBill.Taylor@Sun.COM 
13799517SBill.Taylor@Sun.COM 		/*
13809517SBill.Taylor@Sun.COM 		 * Build the Unreliable Datagram Segment for the WQE, using
13819517SBill.Taylor@Sun.COM 		 * the information from the address handle and the work
13829517SBill.Taylor@Sun.COM 		 * request.
13839517SBill.Taylor@Sun.COM 		 */
13849517SBill.Taylor@Sun.COM 		/* mutex_enter(&ah->ah_lock); */
13859517SBill.Taylor@Sun.COM 		if (wr->wr_opcode == IBT_WRC_SEND) {
13869517SBill.Taylor@Sun.COM 			HERMON_WQE_BUILD_UD(qp, ud, ah, wr->wr.ud.udwr_dest);
13879517SBill.Taylor@Sun.COM 		} else {	/* IBT_WRC_SEND_LSO */
13889517SBill.Taylor@Sun.COM 			HERMON_WQE_BUILD_UD(qp, ud, ah,
13899517SBill.Taylor@Sun.COM 			    wr->wr.ud_lso.lso_ud_dest);
13909517SBill.Taylor@Sun.COM 		}
13919517SBill.Taylor@Sun.COM 		/* mutex_exit(&ah->ah_lock); */
13929517SBill.Taylor@Sun.COM 
13939517SBill.Taylor@Sun.COM 		/* Update "ds" for filling in Data Segments (below) */
13949517SBill.Taylor@Sun.COM 		ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)ud +
13959517SBill.Taylor@Sun.COM 		    sizeof (hermon_hw_snd_wqe_ud_t));
13969517SBill.Taylor@Sun.COM 
13979517SBill.Taylor@Sun.COM 		if (wr->wr_opcode == IBT_WRC_SEND_LSO) {
13989517SBill.Taylor@Sun.COM 			int total_len;
13999517SBill.Taylor@Sun.COM 
14009517SBill.Taylor@Sun.COM 			total_len = (4 + 0xf + wr->wr.ud_lso.lso_hdr_sz) & ~0xf;
14019517SBill.Taylor@Sun.COM 			if ((uintptr_t)ds + total_len + (nds * 16) >
14029517SBill.Taylor@Sun.COM 			    (uintptr_t)desc + (1 << qp->qp_sq_log_wqesz))
14039517SBill.Taylor@Sun.COM 				return (IBT_QP_SGL_LEN_INVALID);
14049517SBill.Taylor@Sun.COM 
14059517SBill.Taylor@Sun.COM 			bcopy(wr->wr.ud_lso.lso_hdr, (uint32_t *)ds + 1,
14069517SBill.Taylor@Sun.COM 			    wr->wr.ud_lso.lso_hdr_sz);
14079517SBill.Taylor@Sun.COM 			old_ds = ds;
14089517SBill.Taylor@Sun.COM 			ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)ds + total_len);
14099517SBill.Taylor@Sun.COM 			for (; i < nds; i++) {
14109517SBill.Taylor@Sun.COM 				if (sgl[i].ds_len == 0)
14119517SBill.Taylor@Sun.COM 					continue;
14129517SBill.Taylor@Sun.COM 				HERMON_WQE_BUILD_DATA_SEG_SEND(&ds[num_ds],
14139517SBill.Taylor@Sun.COM 				    &sgl[i]);
14149517SBill.Taylor@Sun.COM 				num_ds++;
14159517SBill.Taylor@Sun.COM 				i++;
14169517SBill.Taylor@Sun.COM 				break;
14179517SBill.Taylor@Sun.COM 			}
14189517SBill.Taylor@Sun.COM 			membar_producer();
14199517SBill.Taylor@Sun.COM 			HERMON_WQE_BUILD_LSO(qp, old_ds, wr->wr.ud_lso.lso_mss,
14209517SBill.Taylor@Sun.COM 			    wr->wr.ud_lso.lso_hdr_sz);
14219517SBill.Taylor@Sun.COM 		}
14229517SBill.Taylor@Sun.COM 
14239517SBill.Taylor@Sun.COM 		break;
14249517SBill.Taylor@Sun.COM 
14259517SBill.Taylor@Sun.COM 	case IBT_RC_SRV:
14269517SBill.Taylor@Sun.COM 		/* Ensure that work request transport type matches QP type */
14279517SBill.Taylor@Sun.COM 		if (qp->qp_serv_type != HERMON_QP_RC) {
14289517SBill.Taylor@Sun.COM 			return (IBT_QP_SRV_TYPE_INVALID);
14299517SBill.Taylor@Sun.COM 		}
14309517SBill.Taylor@Sun.COM 
14319517SBill.Taylor@Sun.COM 		/*
14329517SBill.Taylor@Sun.COM 		 * Validate the operation type.  For RC requests, we allow
14339517SBill.Taylor@Sun.COM 		 * "Send", "RDMA Read", "RDMA Write", various "Atomic"
14349517SBill.Taylor@Sun.COM 		 * operations, and memory window "Bind"
14359517SBill.Taylor@Sun.COM 		 */
14369517SBill.Taylor@Sun.COM 		if ((wr->wr_opcode != IBT_WRC_SEND) &&
14379517SBill.Taylor@Sun.COM 		    (wr->wr_opcode != IBT_WRC_RDMAR) &&
14389517SBill.Taylor@Sun.COM 		    (wr->wr_opcode != IBT_WRC_RDMAW) &&
14399517SBill.Taylor@Sun.COM 		    (wr->wr_opcode != IBT_WRC_CSWAP) &&
14409517SBill.Taylor@Sun.COM 		    (wr->wr_opcode != IBT_WRC_FADD) &&
14419517SBill.Taylor@Sun.COM 		    (wr->wr_opcode != IBT_WRC_BIND)) {
14429517SBill.Taylor@Sun.COM 			return (IBT_QP_OP_TYPE_INVALID);
14439517SBill.Taylor@Sun.COM 		}
14449517SBill.Taylor@Sun.COM 
14459517SBill.Taylor@Sun.COM 		/*
14469517SBill.Taylor@Sun.COM 		 * If this is a Send request, then all we need to do is break
14479517SBill.Taylor@Sun.COM 		 * out and here and begin the Data Segment processing below
14489517SBill.Taylor@Sun.COM 		 */
14499517SBill.Taylor@Sun.COM 		if (wr->wr_opcode == IBT_WRC_SEND) {
14509517SBill.Taylor@Sun.COM 			break;
14519517SBill.Taylor@Sun.COM 		}
14529517SBill.Taylor@Sun.COM 
14539517SBill.Taylor@Sun.COM 		/*
14549517SBill.Taylor@Sun.COM 		 * If this is an RDMA Read or RDMA Write request, then fill
14559517SBill.Taylor@Sun.COM 		 * in the "Remote Address" header fields.
14569517SBill.Taylor@Sun.COM 		 */
14579517SBill.Taylor@Sun.COM 		if ((wr->wr_opcode == IBT_WRC_RDMAR) ||
14589517SBill.Taylor@Sun.COM 		    (wr->wr_opcode == IBT_WRC_RDMAW)) {
14599517SBill.Taylor@Sun.COM 			rc = (hermon_hw_snd_wqe_remaddr_t *)((uintptr_t)desc +
14609517SBill.Taylor@Sun.COM 			    sizeof (hermon_hw_snd_wqe_ctrl_t));
14619517SBill.Taylor@Sun.COM 
14629517SBill.Taylor@Sun.COM 			/*
14639517SBill.Taylor@Sun.COM 			 * Build the Remote Address Segment for the WQE, using
14649517SBill.Taylor@Sun.COM 			 * the information from the RC work request.
14659517SBill.Taylor@Sun.COM 			 */
14669517SBill.Taylor@Sun.COM 			HERMON_WQE_BUILD_REMADDR(qp, rc, &wr->wr.rc.rcwr.rdma);
14679517SBill.Taylor@Sun.COM 
14689517SBill.Taylor@Sun.COM 			/* Update "ds" for filling in Data Segments (below) */
14699517SBill.Taylor@Sun.COM 			ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)rc +
14709517SBill.Taylor@Sun.COM 			    sizeof (hermon_hw_snd_wqe_remaddr_t));
14719517SBill.Taylor@Sun.COM 			break;
14729517SBill.Taylor@Sun.COM 		}
14739517SBill.Taylor@Sun.COM 
14749517SBill.Taylor@Sun.COM 		/*
14759517SBill.Taylor@Sun.COM 		 * If this is one of the Atomic type operations (i.e
14769517SBill.Taylor@Sun.COM 		 * Compare-Swap or Fetch-Add), then fill in both the "Remote
14779517SBill.Taylor@Sun.COM 		 * Address" header fields and the "Atomic" header fields.
14789517SBill.Taylor@Sun.COM 		 */
14799517SBill.Taylor@Sun.COM 		if ((wr->wr_opcode == IBT_WRC_CSWAP) ||
14809517SBill.Taylor@Sun.COM 		    (wr->wr_opcode == IBT_WRC_FADD)) {
14819517SBill.Taylor@Sun.COM 			rc = (hermon_hw_snd_wqe_remaddr_t *)((uintptr_t)desc +
14829517SBill.Taylor@Sun.COM 			    sizeof (hermon_hw_snd_wqe_ctrl_t));
14839517SBill.Taylor@Sun.COM 			at = (hermon_hw_snd_wqe_atomic_t *)((uintptr_t)rc +
14849517SBill.Taylor@Sun.COM 			    sizeof (hermon_hw_snd_wqe_remaddr_t));
14859517SBill.Taylor@Sun.COM 
14869517SBill.Taylor@Sun.COM 			/*
14879517SBill.Taylor@Sun.COM 			 * Build the Remote Address and Atomic Segments for
14889517SBill.Taylor@Sun.COM 			 * the WQE, using the information from the RC Atomic
14899517SBill.Taylor@Sun.COM 			 * work request.
14909517SBill.Taylor@Sun.COM 			 */
14919517SBill.Taylor@Sun.COM 			HERMON_WQE_BUILD_RC_ATOMIC_REMADDR(qp, rc, wr);
14929517SBill.Taylor@Sun.COM 			HERMON_WQE_BUILD_ATOMIC(qp, at, wr->wr.rc.rcwr.atomic);
14939517SBill.Taylor@Sun.COM 
14949517SBill.Taylor@Sun.COM 			/* Update "ds" for filling in Data Segments (below) */
14959517SBill.Taylor@Sun.COM 			ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)at +
14969517SBill.Taylor@Sun.COM 			    sizeof (hermon_hw_snd_wqe_atomic_t));
14979517SBill.Taylor@Sun.COM 
14989517SBill.Taylor@Sun.COM 			/*
14999517SBill.Taylor@Sun.COM 			 * Update "nds" and "sgl" because Atomic requests have
15009517SBill.Taylor@Sun.COM 			 * only a single Data Segment (and they are encoded
15019517SBill.Taylor@Sun.COM 			 * somewhat differently in the work request.
15029517SBill.Taylor@Sun.COM 			 */
15039517SBill.Taylor@Sun.COM 			nds = 1;
15049517SBill.Taylor@Sun.COM 			sgl = wr->wr_sgl;
15059517SBill.Taylor@Sun.COM 			break;
15069517SBill.Taylor@Sun.COM 		}
15079517SBill.Taylor@Sun.COM 
15089517SBill.Taylor@Sun.COM 		/*
15099517SBill.Taylor@Sun.COM 		 * If this is memory window Bind operation, then we call the
15109517SBill.Taylor@Sun.COM 		 * hermon_wr_bind_check() routine to validate the request and
15119517SBill.Taylor@Sun.COM 		 * to generate the updated RKey.  If this is successful, then
15129517SBill.Taylor@Sun.COM 		 * we fill in the WQE's "Bind" header fields.
15139517SBill.Taylor@Sun.COM 		 */
15149517SBill.Taylor@Sun.COM 		if (wr->wr_opcode == IBT_WRC_BIND) {
15159517SBill.Taylor@Sun.COM 			status = hermon_wr_bind_check(state, wr);
15169517SBill.Taylor@Sun.COM 			if (status != DDI_SUCCESS) {
15179517SBill.Taylor@Sun.COM 				return (status);
15189517SBill.Taylor@Sun.COM 			}
15199517SBill.Taylor@Sun.COM 
15209517SBill.Taylor@Sun.COM 			bn = (hermon_hw_snd_wqe_bind_t *)((uintptr_t)desc +
15219517SBill.Taylor@Sun.COM 			    sizeof (hermon_hw_snd_wqe_ctrl_t));
15229517SBill.Taylor@Sun.COM 
15239517SBill.Taylor@Sun.COM 			/*
15249517SBill.Taylor@Sun.COM 			 * Build the Bind Memory Window Segments for the WQE,
15259517SBill.Taylor@Sun.COM 			 * using the information from the RC Bind memory
15269517SBill.Taylor@Sun.COM 			 * window work request.
15279517SBill.Taylor@Sun.COM 			 */
15289517SBill.Taylor@Sun.COM 			HERMON_WQE_BUILD_BIND(qp, bn, wr->wr.rc.rcwr.bind);
15299517SBill.Taylor@Sun.COM 
15309517SBill.Taylor@Sun.COM 			/*
15319517SBill.Taylor@Sun.COM 			 * Update the "ds" pointer.  Even though the "bind"
15329517SBill.Taylor@Sun.COM 			 * operation requires no SGLs, this is necessary to
15339517SBill.Taylor@Sun.COM 			 * facilitate the correct descriptor size calculations
15349517SBill.Taylor@Sun.COM 			 * (below).
15359517SBill.Taylor@Sun.COM 			 */
15369517SBill.Taylor@Sun.COM 			ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)bn +
15379517SBill.Taylor@Sun.COM 			    sizeof (hermon_hw_snd_wqe_bind_t));
15389517SBill.Taylor@Sun.COM 			nds = 0;
15399517SBill.Taylor@Sun.COM 		}
15409517SBill.Taylor@Sun.COM 		break;
15419517SBill.Taylor@Sun.COM 
15429517SBill.Taylor@Sun.COM 	case IBT_UC_SRV:
15439517SBill.Taylor@Sun.COM 		/* Ensure that work request transport type matches QP type */
15449517SBill.Taylor@Sun.COM 		if (qp->qp_serv_type != HERMON_QP_UC) {
15459517SBill.Taylor@Sun.COM 			return (IBT_QP_SRV_TYPE_INVALID);
15469517SBill.Taylor@Sun.COM 		}
15479517SBill.Taylor@Sun.COM 
15489517SBill.Taylor@Sun.COM 		/*
15499517SBill.Taylor@Sun.COM 		 * Validate the operation type.  For UC requests, we only
15509517SBill.Taylor@Sun.COM 		 * allow "Send", "RDMA Write", and memory window "Bind".
15519517SBill.Taylor@Sun.COM 		 * Note: Unlike RC, UC does not allow "RDMA Read" or "Atomic"
15529517SBill.Taylor@Sun.COM 		 * operations
15539517SBill.Taylor@Sun.COM 		 */
15549517SBill.Taylor@Sun.COM 		if ((wr->wr_opcode != IBT_WRC_SEND) &&
15559517SBill.Taylor@Sun.COM 		    (wr->wr_opcode != IBT_WRC_RDMAW) &&
15569517SBill.Taylor@Sun.COM 		    (wr->wr_opcode != IBT_WRC_BIND)) {
15579517SBill.Taylor@Sun.COM 			return (IBT_QP_OP_TYPE_INVALID);
15589517SBill.Taylor@Sun.COM 		}
15599517SBill.Taylor@Sun.COM 
15609517SBill.Taylor@Sun.COM 		/*
15619517SBill.Taylor@Sun.COM 		 * If this is a Send request, then all we need to do is break
15629517SBill.Taylor@Sun.COM 		 * out and here and begin the Data Segment processing below
15639517SBill.Taylor@Sun.COM 		 */
15649517SBill.Taylor@Sun.COM 		if (wr->wr_opcode == IBT_WRC_SEND) {
15659517SBill.Taylor@Sun.COM 			break;
15669517SBill.Taylor@Sun.COM 		}
15679517SBill.Taylor@Sun.COM 
15689517SBill.Taylor@Sun.COM 		/*
15699517SBill.Taylor@Sun.COM 		 * If this is an RDMA Write request, then fill in the "Remote
15709517SBill.Taylor@Sun.COM 		 * Address" header fields.
15719517SBill.Taylor@Sun.COM 		 */
15729517SBill.Taylor@Sun.COM 		if (wr->wr_opcode == IBT_WRC_RDMAW) {
15739517SBill.Taylor@Sun.COM 			uc = (hermon_hw_snd_wqe_remaddr_t *)((uintptr_t)desc +
15749517SBill.Taylor@Sun.COM 			    sizeof (hermon_hw_snd_wqe_ctrl_t));
15759517SBill.Taylor@Sun.COM 
15769517SBill.Taylor@Sun.COM 			/*
15779517SBill.Taylor@Sun.COM 			 * Build the Remote Address Segment for the WQE, using
15789517SBill.Taylor@Sun.COM 			 * the information from the UC work request.
15799517SBill.Taylor@Sun.COM 			 */
15809517SBill.Taylor@Sun.COM 			HERMON_WQE_BUILD_REMADDR(qp, uc, &wr->wr.uc.ucwr.rdma);
15819517SBill.Taylor@Sun.COM 
15829517SBill.Taylor@Sun.COM 			/* Update "ds" for filling in Data Segments (below) */
15839517SBill.Taylor@Sun.COM 			ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)uc +
15849517SBill.Taylor@Sun.COM 			    sizeof (hermon_hw_snd_wqe_remaddr_t));
15859517SBill.Taylor@Sun.COM 			break;
15869517SBill.Taylor@Sun.COM 		}
15879517SBill.Taylor@Sun.COM 
15889517SBill.Taylor@Sun.COM 		/*
15899517SBill.Taylor@Sun.COM 		 * If this is memory window Bind operation, then we call the
15909517SBill.Taylor@Sun.COM 		 * hermon_wr_bind_check() routine to validate the request and
15919517SBill.Taylor@Sun.COM 		 * to generate the updated RKey.  If this is successful, then
15929517SBill.Taylor@Sun.COM 		 * we fill in the WQE's "Bind" header fields.
15939517SBill.Taylor@Sun.COM 		 */
15949517SBill.Taylor@Sun.COM 		if (wr->wr_opcode == IBT_WRC_BIND) {
15959517SBill.Taylor@Sun.COM 			status = hermon_wr_bind_check(state, wr);
15969517SBill.Taylor@Sun.COM 			if (status != DDI_SUCCESS) {
15979517SBill.Taylor@Sun.COM 				return (status);
15989517SBill.Taylor@Sun.COM 			}
15999517SBill.Taylor@Sun.COM 
16009517SBill.Taylor@Sun.COM 			bn = (hermon_hw_snd_wqe_bind_t *)((uintptr_t)desc +
16019517SBill.Taylor@Sun.COM 			    sizeof (hermon_hw_snd_wqe_ctrl_t));
16029517SBill.Taylor@Sun.COM 
16039517SBill.Taylor@Sun.COM 			/*
16049517SBill.Taylor@Sun.COM 			 * Build the Bind Memory Window Segments for the WQE,
16059517SBill.Taylor@Sun.COM 			 * using the information from the UC Bind memory
16069517SBill.Taylor@Sun.COM 			 * window work request.
16079517SBill.Taylor@Sun.COM 			 */
16089517SBill.Taylor@Sun.COM 			HERMON_WQE_BUILD_BIND(qp, bn, wr->wr.uc.ucwr.bind);
16099517SBill.Taylor@Sun.COM 
16109517SBill.Taylor@Sun.COM 			/*
16119517SBill.Taylor@Sun.COM 			 * Update the "ds" pointer.  Even though the "bind"
16129517SBill.Taylor@Sun.COM 			 * operation requires no SGLs, this is necessary to
16139517SBill.Taylor@Sun.COM 			 * facilitate the correct descriptor size calculations
16149517SBill.Taylor@Sun.COM 			 * (below).
16159517SBill.Taylor@Sun.COM 			 */
16169517SBill.Taylor@Sun.COM 			ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)bn +
16179517SBill.Taylor@Sun.COM 			    sizeof (hermon_hw_snd_wqe_bind_t));
16189517SBill.Taylor@Sun.COM 			nds = 0;
16199517SBill.Taylor@Sun.COM 		}
16209517SBill.Taylor@Sun.COM 		break;
16219517SBill.Taylor@Sun.COM 
16229517SBill.Taylor@Sun.COM 	default:
16239517SBill.Taylor@Sun.COM 		return (IBT_QP_SRV_TYPE_INVALID);
16249517SBill.Taylor@Sun.COM 	}
16259517SBill.Taylor@Sun.COM 
16269517SBill.Taylor@Sun.COM 	/*
16279517SBill.Taylor@Sun.COM 	 * Now fill in the Data Segments (SGL) for the Send WQE based on
16289517SBill.Taylor@Sun.COM 	 * the values setup above (i.e. "sgl", "nds", and the "ds" pointer
16299517SBill.Taylor@Sun.COM 	 * Start by checking for a valid number of SGL entries
16309517SBill.Taylor@Sun.COM 	 */
16319517SBill.Taylor@Sun.COM 	if (nds > qp->qp_sq_sgl) {
16329517SBill.Taylor@Sun.COM 		return (IBT_QP_SGL_LEN_INVALID);
16339517SBill.Taylor@Sun.COM 	}
16349517SBill.Taylor@Sun.COM 
16359517SBill.Taylor@Sun.COM 	/*
16369517SBill.Taylor@Sun.COM 	 * For each SGL in the Send Work Request, fill in the Send WQE's data
16379517SBill.Taylor@Sun.COM 	 * segments.  Note: We skip any SGL with zero size because Hermon
16389517SBill.Taylor@Sun.COM 	 * hardware cannot handle a zero for "byte_cnt" in the WQE.  Actually
16399517SBill.Taylor@Sun.COM 	 * the encoding for zero means a 2GB transfer.
16409517SBill.Taylor@Sun.COM 	 */
16419517SBill.Taylor@Sun.COM 	for (last_ds = num_ds, j = i; j < nds; j++) {
16429517SBill.Taylor@Sun.COM 		if (sgl[j].ds_len != 0)
16439517SBill.Taylor@Sun.COM 			last_ds++;	/* real last ds of wqe to fill */
16449517SBill.Taylor@Sun.COM 	}
16459517SBill.Taylor@Sun.COM 
16469517SBill.Taylor@Sun.COM 	/*
16479517SBill.Taylor@Sun.COM 	 * Return the size of descriptor (in 16-byte chunks)
16489517SBill.Taylor@Sun.COM 	 * For Hermon, we want them (for now) to be on stride size
16499517SBill.Taylor@Sun.COM 	 * boundaries, which was implicit in Tavor/Arbel
16509517SBill.Taylor@Sun.COM 	 *
16519517SBill.Taylor@Sun.COM 	 */
16529517SBill.Taylor@Sun.COM 	tmpsize = ((uintptr_t)&ds[last_ds] - (uintptr_t)desc);
16539517SBill.Taylor@Sun.COM 
16549517SBill.Taylor@Sun.COM 	*size = tmpsize >> 0x4;
16559517SBill.Taylor@Sun.COM 
16569517SBill.Taylor@Sun.COM 	for (j = nds; --j >= i; ) {
16579517SBill.Taylor@Sun.COM 		if (sgl[j].ds_len == 0) {
16589517SBill.Taylor@Sun.COM 			continue;
16599517SBill.Taylor@Sun.COM 		}
16609517SBill.Taylor@Sun.COM 
16619517SBill.Taylor@Sun.COM 		/*
16629517SBill.Taylor@Sun.COM 		 * Fill in the Data Segment(s) for the current WQE, using the
16639517SBill.Taylor@Sun.COM 		 * information contained in the scatter-gather list of the
16649517SBill.Taylor@Sun.COM 		 * work request.
16659517SBill.Taylor@Sun.COM 		 */
16669517SBill.Taylor@Sun.COM 		last_ds--;
16679517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_DATA_SEG_SEND(&ds[last_ds], &sgl[j]);
16689517SBill.Taylor@Sun.COM 	}
16699517SBill.Taylor@Sun.COM 
16709517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
16719517SBill.Taylor@Sun.COM }
16729517SBill.Taylor@Sun.COM 
16739517SBill.Taylor@Sun.COM 
16749517SBill.Taylor@Sun.COM 
16759517SBill.Taylor@Sun.COM /*
16769517SBill.Taylor@Sun.COM  * hermon_wqe_mlx_build()
16779517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
16789517SBill.Taylor@Sun.COM  */
16799517SBill.Taylor@Sun.COM static int
hermon_wqe_mlx_build(hermon_state_t * state,hermon_qphdl_t qp,ibt_send_wr_t * wr,uint64_t * desc,uint_t * size)16809517SBill.Taylor@Sun.COM hermon_wqe_mlx_build(hermon_state_t *state, hermon_qphdl_t qp,
16819517SBill.Taylor@Sun.COM     ibt_send_wr_t *wr, uint64_t *desc, uint_t *size)
16829517SBill.Taylor@Sun.COM {
16839517SBill.Taylor@Sun.COM 	hermon_ahhdl_t		ah;
16849517SBill.Taylor@Sun.COM 	hermon_hw_udav_t	*udav;
16859517SBill.Taylor@Sun.COM 	ib_lrh_hdr_t		*lrh;
16869517SBill.Taylor@Sun.COM 	ib_grh_t		*grh;
16879517SBill.Taylor@Sun.COM 	ib_bth_hdr_t		*bth;
16889517SBill.Taylor@Sun.COM 	ib_deth_hdr_t		*deth;
16899517SBill.Taylor@Sun.COM 	hermon_hw_wqe_sgl_t	*ds;
16909517SBill.Taylor@Sun.COM 	ibt_wr_ds_t		*sgl;
16919517SBill.Taylor@Sun.COM 	uint8_t			*mgmtclass, *hpoint, *hcount;
16929517SBill.Taylor@Sun.COM 	uint32_t		nds, offset, pktlen;
16939517SBill.Taylor@Sun.COM 	uint32_t		desc_sz;
16949517SBill.Taylor@Sun.COM 	int			i, num_ds;
16959517SBill.Taylor@Sun.COM 	int			tmpsize;
16969517SBill.Taylor@Sun.COM 
16979517SBill.Taylor@Sun.COM 	ASSERT(MUTEX_HELD(&qp->qp_sq_lock));
16989517SBill.Taylor@Sun.COM 
16999517SBill.Taylor@Sun.COM 	/* Initialize the information for the Data Segments */
17009517SBill.Taylor@Sun.COM 	ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)desc +
17019517SBill.Taylor@Sun.COM 	    sizeof (hermon_hw_mlx_wqe_nextctrl_t));
17029517SBill.Taylor@Sun.COM 
17039517SBill.Taylor@Sun.COM 	/*
17049517SBill.Taylor@Sun.COM 	 * Pull the address handle from the work request. The UDAV will
17059517SBill.Taylor@Sun.COM 	 * be used to answer some questions about the request.
17069517SBill.Taylor@Sun.COM 	 */
17079517SBill.Taylor@Sun.COM 	ah = (hermon_ahhdl_t)wr->wr.ud.udwr_dest->ud_ah;
17089517SBill.Taylor@Sun.COM 	if (ah == NULL) {
17099517SBill.Taylor@Sun.COM 		return (IBT_AH_HDL_INVALID);
17109517SBill.Taylor@Sun.COM 	}
17119517SBill.Taylor@Sun.COM 	mutex_enter(&ah->ah_lock);
17129517SBill.Taylor@Sun.COM 	udav = ah->ah_udav;
17139517SBill.Taylor@Sun.COM 
17149517SBill.Taylor@Sun.COM 	/*
17159517SBill.Taylor@Sun.COM 	 * If the request is for QP1 and the destination LID is equal to
17169517SBill.Taylor@Sun.COM 	 * the Permissive LID, then return an error.  This combination is
17179517SBill.Taylor@Sun.COM 	 * not allowed
17189517SBill.Taylor@Sun.COM 	 */
17199517SBill.Taylor@Sun.COM 	if ((udav->rlid == IB_LID_PERMISSIVE) &&
17209517SBill.Taylor@Sun.COM 	    (qp->qp_is_special == HERMON_QP_GSI)) {
17219517SBill.Taylor@Sun.COM 		mutex_exit(&ah->ah_lock);
17229517SBill.Taylor@Sun.COM 		return (IBT_AH_HDL_INVALID);
17239517SBill.Taylor@Sun.COM 	}
17249517SBill.Taylor@Sun.COM 
17259517SBill.Taylor@Sun.COM 	/*
17269517SBill.Taylor@Sun.COM 	 * Calculate the size of the packet headers, including the GRH
17279517SBill.Taylor@Sun.COM 	 * (if necessary)
17289517SBill.Taylor@Sun.COM 	 */
17299517SBill.Taylor@Sun.COM 	desc_sz = sizeof (ib_lrh_hdr_t) + sizeof (ib_bth_hdr_t) +
17309517SBill.Taylor@Sun.COM 	    sizeof (ib_deth_hdr_t);
17319517SBill.Taylor@Sun.COM 	if (udav->grh) {
17329517SBill.Taylor@Sun.COM 		desc_sz += sizeof (ib_grh_t);
17339517SBill.Taylor@Sun.COM 	}
17349517SBill.Taylor@Sun.COM 
17359517SBill.Taylor@Sun.COM 	/*
17369517SBill.Taylor@Sun.COM 	 * Begin to build the first "inline" data segment for the packet
17379517SBill.Taylor@Sun.COM 	 * headers.  Note:  By specifying "inline" we can build the contents
17389517SBill.Taylor@Sun.COM 	 * of the MAD packet headers directly into the work queue (as part
17399517SBill.Taylor@Sun.COM 	 * descriptor).  This has the advantage of both speeding things up
17409517SBill.Taylor@Sun.COM 	 * and of not requiring the driver to allocate/register any additional
17419517SBill.Taylor@Sun.COM 	 * memory for the packet headers.
17429517SBill.Taylor@Sun.COM 	 */
17439517SBill.Taylor@Sun.COM 	HERMON_WQE_BUILD_INLINE(qp, &ds[0], desc_sz);
17449517SBill.Taylor@Sun.COM 	desc_sz += 4;
17459517SBill.Taylor@Sun.COM 
17469517SBill.Taylor@Sun.COM 	/*
17479517SBill.Taylor@Sun.COM 	 * Build Local Route Header (LRH)
17489517SBill.Taylor@Sun.COM 	 *    We start here by building the LRH into a temporary location.
17499517SBill.Taylor@Sun.COM 	 *    When we have finished we copy the LRH data into the descriptor.
17509517SBill.Taylor@Sun.COM 	 *
17519517SBill.Taylor@Sun.COM 	 *    Notice that the VL values are hardcoded.  This is not a problem
17529517SBill.Taylor@Sun.COM 	 *    because VL15 is decided later based on the value in the MLX
17539517SBill.Taylor@Sun.COM 	 *    transport "next/ctrl" header (see the "vl15" bit below), and it
17549517SBill.Taylor@Sun.COM 	 *    is otherwise (meaning for QP1) chosen from the SL-to-VL table
17559517SBill.Taylor@Sun.COM 	 *    values.  This rule does not hold for loopback packets however
17569517SBill.Taylor@Sun.COM 	 *    (all of which bypass the SL-to-VL tables) and it is the reason
17579517SBill.Taylor@Sun.COM 	 *    that non-QP0 MADs are setup with VL hardcoded to zero below.
17589517SBill.Taylor@Sun.COM 	 *
17599517SBill.Taylor@Sun.COM 	 *    Notice also that Source LID is hardcoded to the Permissive LID
17609517SBill.Taylor@Sun.COM 	 *    (0xFFFF).  This is also not a problem because if the Destination
17619517SBill.Taylor@Sun.COM 	 *    LID is not the Permissive LID, then the "slr" value in the MLX
17629517SBill.Taylor@Sun.COM 	 *    transport "next/ctrl" header will be set to zero and the hardware
17639517SBill.Taylor@Sun.COM 	 *    will pull the LID from value in the port.
17649517SBill.Taylor@Sun.COM 	 */
17659517SBill.Taylor@Sun.COM 	lrh = (ib_lrh_hdr_t *)((uintptr_t)&ds[0] + 4);
17669517SBill.Taylor@Sun.COM 	pktlen = (desc_sz + 0x100) >> 2;
17679517SBill.Taylor@Sun.COM 	HERMON_WQE_BUILD_MLX_LRH(lrh, qp, udav, pktlen);
17689517SBill.Taylor@Sun.COM 
17699517SBill.Taylor@Sun.COM 	/*
17709517SBill.Taylor@Sun.COM 	 * Build Global Route Header (GRH)
17719517SBill.Taylor@Sun.COM 	 *    This is only built if necessary as defined by the "grh" bit in
17729517SBill.Taylor@Sun.COM 	 *    the address vector.  Note:  We also calculate the offset to the
17739517SBill.Taylor@Sun.COM 	 *    next header (BTH) based on whether or not the "grh" bit is set.
17749517SBill.Taylor@Sun.COM 	 */
17759517SBill.Taylor@Sun.COM 	if (udav->grh) {
17769517SBill.Taylor@Sun.COM 		/*
17779517SBill.Taylor@Sun.COM 		 * If the request is for QP0, then return an error.  The
17789517SBill.Taylor@Sun.COM 		 * combination of global routine (GRH) and QP0 is not allowed.
17799517SBill.Taylor@Sun.COM 		 */
17809517SBill.Taylor@Sun.COM 		if (qp->qp_is_special == HERMON_QP_SMI) {
17819517SBill.Taylor@Sun.COM 			mutex_exit(&ah->ah_lock);
17829517SBill.Taylor@Sun.COM 			return (IBT_AH_HDL_INVALID);
17839517SBill.Taylor@Sun.COM 		}
17849517SBill.Taylor@Sun.COM 		grh = (ib_grh_t *)((uintptr_t)lrh + sizeof (ib_lrh_hdr_t));
17859517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_MLX_GRH(state, grh, qp, udav, pktlen);
17869517SBill.Taylor@Sun.COM 
17879517SBill.Taylor@Sun.COM 		bth = (ib_bth_hdr_t *)((uintptr_t)grh + sizeof (ib_grh_t));
17889517SBill.Taylor@Sun.COM 	} else {
17899517SBill.Taylor@Sun.COM 		bth = (ib_bth_hdr_t *)((uintptr_t)lrh + sizeof (ib_lrh_hdr_t));
17909517SBill.Taylor@Sun.COM 	}
17919517SBill.Taylor@Sun.COM 	mutex_exit(&ah->ah_lock);
17929517SBill.Taylor@Sun.COM 
17939517SBill.Taylor@Sun.COM 
17949517SBill.Taylor@Sun.COM 	/*
17959517SBill.Taylor@Sun.COM 	 * Build Base Transport Header (BTH)
17969517SBill.Taylor@Sun.COM 	 *    Notice that the M, PadCnt, and TVer fields are all set
17979517SBill.Taylor@Sun.COM 	 *    to zero implicitly.  This is true for all Management Datagrams
17989517SBill.Taylor@Sun.COM 	 *    MADs whether GSI are SMI.
17999517SBill.Taylor@Sun.COM 	 */
18009517SBill.Taylor@Sun.COM 	HERMON_WQE_BUILD_MLX_BTH(state, bth, qp, wr);
18019517SBill.Taylor@Sun.COM 
18029517SBill.Taylor@Sun.COM 	/*
18039517SBill.Taylor@Sun.COM 	 * Build Datagram Extended Transport Header (DETH)
18049517SBill.Taylor@Sun.COM 	 */
18059517SBill.Taylor@Sun.COM 	deth = (ib_deth_hdr_t *)((uintptr_t)bth + sizeof (ib_bth_hdr_t));
18069517SBill.Taylor@Sun.COM 	HERMON_WQE_BUILD_MLX_DETH(deth, qp);
18079517SBill.Taylor@Sun.COM 
18089517SBill.Taylor@Sun.COM 	/* Ensure that the Data Segment is aligned on a 16-byte boundary */
18099517SBill.Taylor@Sun.COM 	ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)deth + sizeof (ib_deth_hdr_t));
18109517SBill.Taylor@Sun.COM 	ds = (hermon_hw_wqe_sgl_t *)(((uintptr_t)ds + 0xF) & ~0xF);
18119517SBill.Taylor@Sun.COM 	nds = wr->wr_nds;
18129517SBill.Taylor@Sun.COM 	sgl = wr->wr_sgl;
18139517SBill.Taylor@Sun.COM 	num_ds = 0;
18149517SBill.Taylor@Sun.COM 
18159517SBill.Taylor@Sun.COM 	/*
18169517SBill.Taylor@Sun.COM 	 * Now fill in the Data Segments (SGL) for the MLX WQE based on the
18179517SBill.Taylor@Sun.COM 	 * values set up above (i.e. "sgl", "nds", and the "ds" pointer
18189517SBill.Taylor@Sun.COM 	 * Start by checking for a valid number of SGL entries
18199517SBill.Taylor@Sun.COM 	 */
18209517SBill.Taylor@Sun.COM 	if (nds > qp->qp_sq_sgl) {
18219517SBill.Taylor@Sun.COM 		return (IBT_QP_SGL_LEN_INVALID);
18229517SBill.Taylor@Sun.COM 	}
18239517SBill.Taylor@Sun.COM 
18249517SBill.Taylor@Sun.COM 	/*
18259517SBill.Taylor@Sun.COM 	 * For each SGL in the Send Work Request, fill in the MLX WQE's data
18269517SBill.Taylor@Sun.COM 	 * segments.  Note: We skip any SGL with zero size because Hermon
18279517SBill.Taylor@Sun.COM 	 * hardware cannot handle a zero for "byte_cnt" in the WQE.  Actually
18289517SBill.Taylor@Sun.COM 	 * the encoding for zero means a 2GB transfer.  Because of this special
18299517SBill.Taylor@Sun.COM 	 * encoding in the hardware, we mask the requested length with
18309517SBill.Taylor@Sun.COM 	 * HERMON_WQE_SGL_BYTE_CNT_MASK (so that 2GB will end up encoded as
18319517SBill.Taylor@Sun.COM 	 * zero.)
18329517SBill.Taylor@Sun.COM 	 */
18339517SBill.Taylor@Sun.COM 	mgmtclass = hpoint = hcount = NULL;
18349517SBill.Taylor@Sun.COM 	offset = 0;
18359517SBill.Taylor@Sun.COM 	for (i = 0; i < nds; i++) {
18369517SBill.Taylor@Sun.COM 		if (sgl[i].ds_len == 0) {
18379517SBill.Taylor@Sun.COM 			continue;
18389517SBill.Taylor@Sun.COM 		}
18399517SBill.Taylor@Sun.COM 
18409517SBill.Taylor@Sun.COM 		/*
18419517SBill.Taylor@Sun.COM 		 * Fill in the Data Segment(s) for the MLX send WQE, using
18429517SBill.Taylor@Sun.COM 		 * the information contained in the scatter-gather list of
18439517SBill.Taylor@Sun.COM 		 * the work request.
18449517SBill.Taylor@Sun.COM 		 */
18459517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_DATA_SEG_SEND(&ds[num_ds], &sgl[i]);
18469517SBill.Taylor@Sun.COM 
18479517SBill.Taylor@Sun.COM 		/*
18489517SBill.Taylor@Sun.COM 		 * Search through the contents of all MADs posted to QP0 to
18499517SBill.Taylor@Sun.COM 		 * initialize pointers to the places where Directed Route "hop
18509517SBill.Taylor@Sun.COM 		 * pointer", "hop count", and "mgmtclass" would be.  Hermon
18519517SBill.Taylor@Sun.COM 		 * needs these updated (i.e. incremented or decremented, as
18529517SBill.Taylor@Sun.COM 		 * necessary) by software.
18539517SBill.Taylor@Sun.COM 		 */
18549517SBill.Taylor@Sun.COM 		if (qp->qp_is_special == HERMON_QP_SMI) {
18559517SBill.Taylor@Sun.COM 
18569517SBill.Taylor@Sun.COM 			HERMON_SPECIAL_QP_DRMAD_GET_MGMTCLASS(mgmtclass,
18579517SBill.Taylor@Sun.COM 			    offset, sgl[i].ds_va, sgl[i].ds_len);
18589517SBill.Taylor@Sun.COM 
18599517SBill.Taylor@Sun.COM 			HERMON_SPECIAL_QP_DRMAD_GET_HOPPOINTER(hpoint,
18609517SBill.Taylor@Sun.COM 			    offset, sgl[i].ds_va, sgl[i].ds_len);
18619517SBill.Taylor@Sun.COM 
18629517SBill.Taylor@Sun.COM 			HERMON_SPECIAL_QP_DRMAD_GET_HOPCOUNT(hcount,
18639517SBill.Taylor@Sun.COM 			    offset, sgl[i].ds_va, sgl[i].ds_len);
18649517SBill.Taylor@Sun.COM 
18659517SBill.Taylor@Sun.COM 			offset += sgl[i].ds_len;
18669517SBill.Taylor@Sun.COM 		}
18679517SBill.Taylor@Sun.COM 		num_ds++;
18689517SBill.Taylor@Sun.COM 	}
18699517SBill.Taylor@Sun.COM 
18709517SBill.Taylor@Sun.COM 	/*
18719517SBill.Taylor@Sun.COM 	 * Hermon's Directed Route MADs need to have the "hop pointer"
18729517SBill.Taylor@Sun.COM 	 * incremented/decremented (as necessary) depending on whether it is
18739517SBill.Taylor@Sun.COM 	 * currently less than or greater than the "hop count" (i.e. whether
18749517SBill.Taylor@Sun.COM 	 * the MAD is a request or a response.)
18759517SBill.Taylor@Sun.COM 	 */
18769517SBill.Taylor@Sun.COM 	if (qp->qp_is_special == HERMON_QP_SMI) {
18779517SBill.Taylor@Sun.COM 		HERMON_SPECIAL_QP_DRMAD_DO_HOPPOINTER_MODIFY(*mgmtclass,
18789517SBill.Taylor@Sun.COM 		    *hpoint, *hcount);
18799517SBill.Taylor@Sun.COM 	}
18809517SBill.Taylor@Sun.COM 
18819517SBill.Taylor@Sun.COM 	/*
18829517SBill.Taylor@Sun.COM 	 * Now fill in the ICRC Data Segment.  This data segment is inlined
18839517SBill.Taylor@Sun.COM 	 * just like the packets headers above, but it is only four bytes and
18849517SBill.Taylor@Sun.COM 	 * set to zero (to indicate that we wish the hardware to generate ICRC.
18859517SBill.Taylor@Sun.COM 	 */
18869517SBill.Taylor@Sun.COM 	HERMON_WQE_BUILD_INLINE_ICRC(qp, &ds[num_ds], 4, 0);
18879517SBill.Taylor@Sun.COM 	num_ds++;
18889517SBill.Taylor@Sun.COM 
18899517SBill.Taylor@Sun.COM 	/*
18909517SBill.Taylor@Sun.COM 	 * Return the size of descriptor (in 16-byte chunks)
18919517SBill.Taylor@Sun.COM 	 * For Hermon, we want them (for now) to be on stride size
18929517SBill.Taylor@Sun.COM 	 * boundaries, which was implicit in Tavor/Arbel
18939517SBill.Taylor@Sun.COM 	 */
18949517SBill.Taylor@Sun.COM 	tmpsize = ((uintptr_t)&ds[num_ds] - (uintptr_t)desc);
18959517SBill.Taylor@Sun.COM 
18969517SBill.Taylor@Sun.COM 	*size = tmpsize >> 0x04;
18979517SBill.Taylor@Sun.COM 
18989517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
18999517SBill.Taylor@Sun.COM }
19009517SBill.Taylor@Sun.COM 
19019517SBill.Taylor@Sun.COM 
19029517SBill.Taylor@Sun.COM 
19039517SBill.Taylor@Sun.COM /*
19049517SBill.Taylor@Sun.COM  * hermon_wqe_recv_build()
19059517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
19069517SBill.Taylor@Sun.COM  */
19079517SBill.Taylor@Sun.COM /* ARGSUSED */
19089517SBill.Taylor@Sun.COM static int
hermon_wqe_recv_build(hermon_state_t * state,hermon_qphdl_t qp,ibt_recv_wr_t * wr,uint64_t * desc)19099517SBill.Taylor@Sun.COM hermon_wqe_recv_build(hermon_state_t *state, hermon_qphdl_t qp,
19109517SBill.Taylor@Sun.COM     ibt_recv_wr_t *wr, uint64_t *desc)
19119517SBill.Taylor@Sun.COM {
19129517SBill.Taylor@Sun.COM 	hermon_hw_wqe_sgl_t	*ds;
19139517SBill.Taylor@Sun.COM 	int			i, num_ds;
19149517SBill.Taylor@Sun.COM 
1915*12965SWilliam.Taylor@Oracle.COM 	ASSERT(MUTEX_HELD(&qp->qp_lock));
19169517SBill.Taylor@Sun.COM 
19179517SBill.Taylor@Sun.COM 	/*
19189517SBill.Taylor@Sun.COM 	 * Fill in the Data Segments (SGL) for the Recv WQE  - don't
19199517SBill.Taylor@Sun.COM 	 * need to have a reserved for the ctrl, there is none on the
19209517SBill.Taylor@Sun.COM 	 * recv queue for hermon, but will need to put an invalid
19219517SBill.Taylor@Sun.COM 	 * (null) scatter pointer per PRM
19229517SBill.Taylor@Sun.COM 	 */
19239517SBill.Taylor@Sun.COM 	ds = (hermon_hw_wqe_sgl_t *)(uintptr_t)desc;
19249517SBill.Taylor@Sun.COM 	num_ds = 0;
19259517SBill.Taylor@Sun.COM 
19269517SBill.Taylor@Sun.COM 	/* Check for valid number of SGL entries */
19279517SBill.Taylor@Sun.COM 	if (wr->wr_nds > qp->qp_rq_sgl) {
19289517SBill.Taylor@Sun.COM 		return (IBT_QP_SGL_LEN_INVALID);
19299517SBill.Taylor@Sun.COM 	}
19309517SBill.Taylor@Sun.COM 
19319517SBill.Taylor@Sun.COM 	/*
19329517SBill.Taylor@Sun.COM 	 * For each SGL in the Recv Work Request, fill in the Recv WQE's data
19339517SBill.Taylor@Sun.COM 	 * segments.  Note: We skip any SGL with zero size because Hermon
19349517SBill.Taylor@Sun.COM 	 * hardware cannot handle a zero for "byte_cnt" in the WQE.  Actually
19359517SBill.Taylor@Sun.COM 	 * the encoding for zero means a 2GB transfer.  Because of this special
19369517SBill.Taylor@Sun.COM 	 * encoding in the hardware, we mask the requested length with
19379517SBill.Taylor@Sun.COM 	 * HERMON_WQE_SGL_BYTE_CNT_MASK (so that 2GB will end up encoded as
19389517SBill.Taylor@Sun.COM 	 * zero.)
19399517SBill.Taylor@Sun.COM 	 */
19409517SBill.Taylor@Sun.COM 	for (i = 0; i < wr->wr_nds; i++) {
19419517SBill.Taylor@Sun.COM 		if (wr->wr_sgl[i].ds_len == 0) {
19429517SBill.Taylor@Sun.COM 			continue;
19439517SBill.Taylor@Sun.COM 		}
19449517SBill.Taylor@Sun.COM 
19459517SBill.Taylor@Sun.COM 		/*
19469517SBill.Taylor@Sun.COM 		 * Fill in the Data Segment(s) for the receive WQE, using the
19479517SBill.Taylor@Sun.COM 		 * information contained in the scatter-gather list of the
19489517SBill.Taylor@Sun.COM 		 * work request.
19499517SBill.Taylor@Sun.COM 		 */
19509517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_DATA_SEG_RECV(&ds[num_ds], &wr->wr_sgl[i]);
19519517SBill.Taylor@Sun.COM 		num_ds++;
19529517SBill.Taylor@Sun.COM 	}
19539517SBill.Taylor@Sun.COM 
19549517SBill.Taylor@Sun.COM 	/* put the null sgl pointer as well if needed */
19559517SBill.Taylor@Sun.COM 	if (num_ds < qp->qp_rq_sgl) {
19569517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_DATA_SEG_RECV(&ds[num_ds], &null_sgl);
19579517SBill.Taylor@Sun.COM 	}
19589517SBill.Taylor@Sun.COM 
19599517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
19609517SBill.Taylor@Sun.COM }
19619517SBill.Taylor@Sun.COM 
19629517SBill.Taylor@Sun.COM 
19639517SBill.Taylor@Sun.COM 
19649517SBill.Taylor@Sun.COM /*
19659517SBill.Taylor@Sun.COM  * hermon_wqe_srq_build()
19669517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
19679517SBill.Taylor@Sun.COM  */
19689517SBill.Taylor@Sun.COM /* ARGSUSED */
19699517SBill.Taylor@Sun.COM static int
hermon_wqe_srq_build(hermon_state_t * state,hermon_srqhdl_t srq,ibt_recv_wr_t * wr,uint64_t * desc)19709517SBill.Taylor@Sun.COM hermon_wqe_srq_build(hermon_state_t *state, hermon_srqhdl_t srq,
19719517SBill.Taylor@Sun.COM     ibt_recv_wr_t *wr, uint64_t *desc)
19729517SBill.Taylor@Sun.COM {
19739517SBill.Taylor@Sun.COM 	hermon_hw_wqe_sgl_t	*ds;
19749517SBill.Taylor@Sun.COM 	int			i, num_ds;
19759517SBill.Taylor@Sun.COM 
19769517SBill.Taylor@Sun.COM 	ASSERT(MUTEX_HELD(&srq->srq_lock));
19779517SBill.Taylor@Sun.COM 
19789517SBill.Taylor@Sun.COM 	/* Fill in the Data Segments (SGL) for the Recv WQE */
19799517SBill.Taylor@Sun.COM 	ds = (hermon_hw_wqe_sgl_t *)((uintptr_t)desc +
19809517SBill.Taylor@Sun.COM 	    sizeof (hermon_hw_srq_wqe_next_t));
19819517SBill.Taylor@Sun.COM 	num_ds = 0;
19829517SBill.Taylor@Sun.COM 
19839517SBill.Taylor@Sun.COM 	/* Check for valid number of SGL entries */
19849517SBill.Taylor@Sun.COM 	if (wr->wr_nds > srq->srq_wq_sgl) {
19859517SBill.Taylor@Sun.COM 		return (IBT_QP_SGL_LEN_INVALID);
19869517SBill.Taylor@Sun.COM 	}
19879517SBill.Taylor@Sun.COM 
19889517SBill.Taylor@Sun.COM 	/*
19899517SBill.Taylor@Sun.COM 	 * For each SGL in the Recv Work Request, fill in the Recv WQE's data
19909517SBill.Taylor@Sun.COM 	 * segments.  Note: We skip any SGL with zero size because Hermon
19919517SBill.Taylor@Sun.COM 	 * hardware cannot handle a zero for "byte_cnt" in the WQE.  Actually
19929517SBill.Taylor@Sun.COM 	 * the encoding for zero means a 2GB transfer.  Because of this special
19939517SBill.Taylor@Sun.COM 	 * encoding in the hardware, we mask the requested length with
19949517SBill.Taylor@Sun.COM 	 * HERMON_WQE_SGL_BYTE_CNT_MASK (so that 2GB will end up encoded as
19959517SBill.Taylor@Sun.COM 	 * zero.)
19969517SBill.Taylor@Sun.COM 	 */
19979517SBill.Taylor@Sun.COM 	for (i = 0; i < wr->wr_nds; i++) {
19989517SBill.Taylor@Sun.COM 		if (wr->wr_sgl[i].ds_len == 0) {
19999517SBill.Taylor@Sun.COM 			continue;
20009517SBill.Taylor@Sun.COM 		}
20019517SBill.Taylor@Sun.COM 
20029517SBill.Taylor@Sun.COM 		/*
20039517SBill.Taylor@Sun.COM 		 * Fill in the Data Segment(s) for the receive WQE, using the
20049517SBill.Taylor@Sun.COM 		 * information contained in the scatter-gather list of the
20059517SBill.Taylor@Sun.COM 		 * work request.
20069517SBill.Taylor@Sun.COM 		 */
20079517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_DATA_SEG_RECV(&ds[num_ds], &wr->wr_sgl[i]);
20089517SBill.Taylor@Sun.COM 		num_ds++;
20099517SBill.Taylor@Sun.COM 	}
20109517SBill.Taylor@Sun.COM 
20119517SBill.Taylor@Sun.COM 	/*
20129517SBill.Taylor@Sun.COM 	 * put in the null sgl pointer as well, if needed
20139517SBill.Taylor@Sun.COM 	 */
20149517SBill.Taylor@Sun.COM 	if (num_ds < srq->srq_wq_sgl) {
20159517SBill.Taylor@Sun.COM 		HERMON_WQE_BUILD_DATA_SEG_RECV(&ds[num_ds], &null_sgl);
20169517SBill.Taylor@Sun.COM 	}
20179517SBill.Taylor@Sun.COM 
20189517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
20199517SBill.Taylor@Sun.COM }
20209517SBill.Taylor@Sun.COM 
20219517SBill.Taylor@Sun.COM 
20229517SBill.Taylor@Sun.COM /*
20239517SBill.Taylor@Sun.COM  * hermon_wr_get_immediate()
20249517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
20259517SBill.Taylor@Sun.COM  */
20269517SBill.Taylor@Sun.COM static uint32_t
hermon_wr_get_immediate(ibt_send_wr_t * wr)20279517SBill.Taylor@Sun.COM hermon_wr_get_immediate(ibt_send_wr_t *wr)
20289517SBill.Taylor@Sun.COM {
20299517SBill.Taylor@Sun.COM 	/*
20309517SBill.Taylor@Sun.COM 	 * This routine extracts the "immediate data" from the appropriate
20319517SBill.Taylor@Sun.COM 	 * location in the IBTF work request.  Because of the way the
20329517SBill.Taylor@Sun.COM 	 * work request structure is defined, the location for this data
20339517SBill.Taylor@Sun.COM 	 * depends on the actual work request operation type.
20349517SBill.Taylor@Sun.COM 	 */
20359517SBill.Taylor@Sun.COM 
20369517SBill.Taylor@Sun.COM 	/* For RDMA Write, test if RC or UC */
20379517SBill.Taylor@Sun.COM 	if (wr->wr_opcode == IBT_WRC_RDMAW) {
20389517SBill.Taylor@Sun.COM 		if (wr->wr_trans == IBT_RC_SRV) {
20399517SBill.Taylor@Sun.COM 			return (wr->wr.rc.rcwr.rdma.rdma_immed);
20409517SBill.Taylor@Sun.COM 		} else {  /* IBT_UC_SRV */
20419517SBill.Taylor@Sun.COM 			return (wr->wr.uc.ucwr.rdma.rdma_immed);
20429517SBill.Taylor@Sun.COM 		}
20439517SBill.Taylor@Sun.COM 	}
20449517SBill.Taylor@Sun.COM 
20459517SBill.Taylor@Sun.COM 	/* For Send, test if RC, UD, or UC */
20469517SBill.Taylor@Sun.COM 	if (wr->wr_opcode == IBT_WRC_SEND) {
20479517SBill.Taylor@Sun.COM 		if (wr->wr_trans == IBT_RC_SRV) {
20489517SBill.Taylor@Sun.COM 			return (wr->wr.rc.rcwr.send_immed);
20499517SBill.Taylor@Sun.COM 		} else if (wr->wr_trans == IBT_UD_SRV) {
20509517SBill.Taylor@Sun.COM 			return (wr->wr.ud.udwr_immed);
20519517SBill.Taylor@Sun.COM 		} else {  /* IBT_UC_SRV */
20529517SBill.Taylor@Sun.COM 			return (wr->wr.uc.ucwr.send_immed);
20539517SBill.Taylor@Sun.COM 		}
20549517SBill.Taylor@Sun.COM 	}
20559517SBill.Taylor@Sun.COM 
20569517SBill.Taylor@Sun.COM 	/*
20579517SBill.Taylor@Sun.COM 	 * If any other type of request, then immediate is undefined
20589517SBill.Taylor@Sun.COM 	 */
20599517SBill.Taylor@Sun.COM 	return (0);
20609517SBill.Taylor@Sun.COM }
20619517SBill.Taylor@Sun.COM 
20629517SBill.Taylor@Sun.COM /*
20639517SBill.Taylor@Sun.COM  * hermon_wqe_headroom()
20649517SBill.Taylor@Sun.COM  *	Context: can be called from interrupt or base, currently only from
20659517SBill.Taylor@Sun.COM  *	base context.
20669517SBill.Taylor@Sun.COM  * Routine that fills in the headroom for the Send Queue
20679517SBill.Taylor@Sun.COM  */
20689517SBill.Taylor@Sun.COM 
20699517SBill.Taylor@Sun.COM static void
hermon_wqe_headroom(uint_t from,hermon_qphdl_t qp)20709517SBill.Taylor@Sun.COM hermon_wqe_headroom(uint_t from, hermon_qphdl_t qp)
20719517SBill.Taylor@Sun.COM {
20729517SBill.Taylor@Sun.COM 	uint32_t	*wqe_start, *wqe_top, *wqe_base, qsize;
20739517SBill.Taylor@Sun.COM 	int		hdrmwqes, wqesizebytes, sectperwqe;
20749517SBill.Taylor@Sun.COM 	uint32_t	invalue;
20759517SBill.Taylor@Sun.COM 	int		i, j;
20769517SBill.Taylor@Sun.COM 
20779517SBill.Taylor@Sun.COM 	qsize	 = qp->qp_sq_bufsz;
20789517SBill.Taylor@Sun.COM 	wqesizebytes = 1 << qp->qp_sq_log_wqesz;
20799517SBill.Taylor@Sun.COM 	sectperwqe = wqesizebytes >> 6; 	/* 64 bytes/section */
20809517SBill.Taylor@Sun.COM 	hdrmwqes = qp->qp_sq_hdrmwqes;
20819517SBill.Taylor@Sun.COM 	wqe_base  = (uint32_t *)HERMON_QP_SQ_ENTRY(qp, 0);
20829517SBill.Taylor@Sun.COM 	wqe_top	  = (uint32_t *)HERMON_QP_SQ_ENTRY(qp, qsize);
20839517SBill.Taylor@Sun.COM 	wqe_start = (uint32_t *)HERMON_QP_SQ_ENTRY(qp, from);
20849517SBill.Taylor@Sun.COM 
20859517SBill.Taylor@Sun.COM 	for (i = 0; i < hdrmwqes; i++)	{
20869517SBill.Taylor@Sun.COM 		for (j = 0; j < sectperwqe; j++) {
20879517SBill.Taylor@Sun.COM 			if (j == 0) {		/* 1st section of wqe */
20889517SBill.Taylor@Sun.COM 				/* perserve ownership bit */
20899517SBill.Taylor@Sun.COM 				invalue = ddi_get32(qp->qp_wqinfo.qa_acchdl,
20909517SBill.Taylor@Sun.COM 				    wqe_start) | 0x7FFFFFFF;
20919517SBill.Taylor@Sun.COM 			} else {
20929517SBill.Taylor@Sun.COM 				/* or just invalidate it */
20939517SBill.Taylor@Sun.COM 				invalue = 0xFFFFFFFF;
20949517SBill.Taylor@Sun.COM 			}
20959517SBill.Taylor@Sun.COM 			ddi_put32(qp->qp_wqinfo.qa_acchdl, wqe_start, invalue);
20969517SBill.Taylor@Sun.COM 			wqe_start += 16;	/* move 64 bytes */
20979517SBill.Taylor@Sun.COM 		}
20989517SBill.Taylor@Sun.COM 		if (wqe_start == wqe_top)	/* hit the end of the queue */
20999517SBill.Taylor@Sun.COM 			wqe_start = wqe_base;	/* wrap to start */
21009517SBill.Taylor@Sun.COM 	}
21019517SBill.Taylor@Sun.COM }
21029517SBill.Taylor@Sun.COM 
21039517SBill.Taylor@Sun.COM /*
21049517SBill.Taylor@Sun.COM  * hermon_wr_bind_check()
21059517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
21069517SBill.Taylor@Sun.COM  */
21079517SBill.Taylor@Sun.COM /* ARGSUSED */
21089517SBill.Taylor@Sun.COM static int
hermon_wr_bind_check(hermon_state_t * state,ibt_send_wr_t * wr)21099517SBill.Taylor@Sun.COM hermon_wr_bind_check(hermon_state_t *state, ibt_send_wr_t *wr)
21109517SBill.Taylor@Sun.COM {
21119517SBill.Taylor@Sun.COM 	ibt_bind_flags_t	bind_flags;
21129517SBill.Taylor@Sun.COM 	uint64_t		vaddr, len;
21139517SBill.Taylor@Sun.COM 	uint64_t		reg_start_addr, reg_end_addr;
21149517SBill.Taylor@Sun.COM 	hermon_mwhdl_t		mw;
21159517SBill.Taylor@Sun.COM 	hermon_mrhdl_t		mr;
21169517SBill.Taylor@Sun.COM 	hermon_rsrc_t		*mpt;
21179517SBill.Taylor@Sun.COM 	uint32_t		new_rkey;
21189517SBill.Taylor@Sun.COM 
21199517SBill.Taylor@Sun.COM 	/* Check for a valid Memory Window handle in the WR */
21209517SBill.Taylor@Sun.COM 	mw = (hermon_mwhdl_t)wr->wr.rc.rcwr.bind->bind_ibt_mw_hdl;
21219517SBill.Taylor@Sun.COM 	if (mw == NULL) {
21229517SBill.Taylor@Sun.COM 		return (IBT_MW_HDL_INVALID);
21239517SBill.Taylor@Sun.COM 	}
21249517SBill.Taylor@Sun.COM 
21259517SBill.Taylor@Sun.COM 	/* Check for a valid Memory Region handle in the WR */
21269517SBill.Taylor@Sun.COM 	mr = (hermon_mrhdl_t)wr->wr.rc.rcwr.bind->bind_ibt_mr_hdl;
21279517SBill.Taylor@Sun.COM 	if (mr == NULL) {
21289517SBill.Taylor@Sun.COM 		return (IBT_MR_HDL_INVALID);
21299517SBill.Taylor@Sun.COM 	}
21309517SBill.Taylor@Sun.COM 
21319517SBill.Taylor@Sun.COM 	mutex_enter(&mr->mr_lock);
21329517SBill.Taylor@Sun.COM 	mutex_enter(&mw->mr_lock);
21339517SBill.Taylor@Sun.COM 
21349517SBill.Taylor@Sun.COM 	/*
21359517SBill.Taylor@Sun.COM 	 * Check here to see if the memory region has already been partially
21369517SBill.Taylor@Sun.COM 	 * deregistered as a result of a hermon_umap_umemlock_cb() callback.
21379517SBill.Taylor@Sun.COM 	 * If so, this is an error, return failure.
21389517SBill.Taylor@Sun.COM 	 */
21399517SBill.Taylor@Sun.COM 	if ((mr->mr_is_umem) && (mr->mr_umemcookie == NULL)) {
21409517SBill.Taylor@Sun.COM 		mutex_exit(&mr->mr_lock);
21419517SBill.Taylor@Sun.COM 		mutex_exit(&mw->mr_lock);
21429517SBill.Taylor@Sun.COM 		return (IBT_MR_HDL_INVALID);
21439517SBill.Taylor@Sun.COM 	}
21449517SBill.Taylor@Sun.COM 
21459517SBill.Taylor@Sun.COM 	/* Check for a valid Memory Window RKey (i.e. a matching RKey) */
21469517SBill.Taylor@Sun.COM 	if (mw->mr_rkey != wr->wr.rc.rcwr.bind->bind_rkey) {
21479517SBill.Taylor@Sun.COM 		mutex_exit(&mr->mr_lock);
21489517SBill.Taylor@Sun.COM 		mutex_exit(&mw->mr_lock);
21499517SBill.Taylor@Sun.COM 		return (IBT_MR_RKEY_INVALID);
21509517SBill.Taylor@Sun.COM 	}
21519517SBill.Taylor@Sun.COM 
21529517SBill.Taylor@Sun.COM 	/* Check for a valid Memory Region LKey (i.e. a matching LKey) */
21539517SBill.Taylor@Sun.COM 	if (mr->mr_lkey != wr->wr.rc.rcwr.bind->bind_lkey) {
21549517SBill.Taylor@Sun.COM 		mutex_exit(&mr->mr_lock);
21559517SBill.Taylor@Sun.COM 		mutex_exit(&mw->mr_lock);
21569517SBill.Taylor@Sun.COM 		return (IBT_MR_LKEY_INVALID);
21579517SBill.Taylor@Sun.COM 	}
21589517SBill.Taylor@Sun.COM 
21599517SBill.Taylor@Sun.COM 	/*
21609517SBill.Taylor@Sun.COM 	 * Now check for valid "vaddr" and "len".  Note:  We don't check the
21619517SBill.Taylor@Sun.COM 	 * "vaddr" range when "len == 0" (i.e. on unbind operations)
21629517SBill.Taylor@Sun.COM 	 */
21639517SBill.Taylor@Sun.COM 	len = wr->wr.rc.rcwr.bind->bind_len;
21649517SBill.Taylor@Sun.COM 	if (len != 0) {
21659517SBill.Taylor@Sun.COM 		vaddr = wr->wr.rc.rcwr.bind->bind_va;
21669517SBill.Taylor@Sun.COM 		reg_start_addr = mr->mr_bindinfo.bi_addr;
21679517SBill.Taylor@Sun.COM 		reg_end_addr   = mr->mr_bindinfo.bi_addr +
21689517SBill.Taylor@Sun.COM 		    (mr->mr_bindinfo.bi_len - 1);
21699517SBill.Taylor@Sun.COM 		if ((vaddr < reg_start_addr) || (vaddr > reg_end_addr)) {
21709517SBill.Taylor@Sun.COM 			mutex_exit(&mr->mr_lock);
21719517SBill.Taylor@Sun.COM 			mutex_exit(&mw->mr_lock);
21729517SBill.Taylor@Sun.COM 			return (IBT_MR_VA_INVALID);
21739517SBill.Taylor@Sun.COM 		}
21749517SBill.Taylor@Sun.COM 		vaddr = (vaddr + len) - 1;
21759517SBill.Taylor@Sun.COM 		if (vaddr > reg_end_addr) {
21769517SBill.Taylor@Sun.COM 			mutex_exit(&mr->mr_lock);
21779517SBill.Taylor@Sun.COM 			mutex_exit(&mw->mr_lock);
21789517SBill.Taylor@Sun.COM 			return (IBT_MR_LEN_INVALID);
21799517SBill.Taylor@Sun.COM 		}
21809517SBill.Taylor@Sun.COM 	}
21819517SBill.Taylor@Sun.COM 
21829517SBill.Taylor@Sun.COM 	/*
21839517SBill.Taylor@Sun.COM 	 * Validate the bind access flags.  Remote Write and Atomic access for
21849517SBill.Taylor@Sun.COM 	 * the Memory Window require that Local Write access be set in the
21859517SBill.Taylor@Sun.COM 	 * corresponding Memory Region.
21869517SBill.Taylor@Sun.COM 	 */
21879517SBill.Taylor@Sun.COM 	bind_flags = wr->wr.rc.rcwr.bind->bind_flags;
21889517SBill.Taylor@Sun.COM 	if (((bind_flags & IBT_WR_BIND_WRITE) ||
21899517SBill.Taylor@Sun.COM 	    (bind_flags & IBT_WR_BIND_ATOMIC)) &&
21909517SBill.Taylor@Sun.COM 	    !(mr->mr_accflag & IBT_MR_LOCAL_WRITE)) {
21919517SBill.Taylor@Sun.COM 		mutex_exit(&mr->mr_lock);
21929517SBill.Taylor@Sun.COM 		mutex_exit(&mw->mr_lock);
21939517SBill.Taylor@Sun.COM 		return (IBT_MR_ACCESS_REQ_INVALID);
21949517SBill.Taylor@Sun.COM 	}
21959517SBill.Taylor@Sun.COM 
21969517SBill.Taylor@Sun.COM 	/* Calculate the new RKey for the Memory Window */
21979517SBill.Taylor@Sun.COM 	mpt = mw->mr_mptrsrcp;
21989517SBill.Taylor@Sun.COM 	new_rkey = hermon_mr_keycalc(mpt->hr_indx);
21999517SBill.Taylor@Sun.COM 	new_rkey = hermon_mr_key_swap(new_rkey);
22009517SBill.Taylor@Sun.COM 
22019517SBill.Taylor@Sun.COM 	wr->wr.rc.rcwr.bind->bind_rkey_out = new_rkey;
22029517SBill.Taylor@Sun.COM 	mw->mr_rkey = new_rkey;
22039517SBill.Taylor@Sun.COM 
22049517SBill.Taylor@Sun.COM 	mutex_exit(&mr->mr_lock);
22059517SBill.Taylor@Sun.COM 	mutex_exit(&mw->mr_lock);
22069517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
22079517SBill.Taylor@Sun.COM }
22089517SBill.Taylor@Sun.COM 
22099517SBill.Taylor@Sun.COM 
22109517SBill.Taylor@Sun.COM /*
22119517SBill.Taylor@Sun.COM  * hermon_wrid_from_reset_handling()
22129517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
22139517SBill.Taylor@Sun.COM  */
22149517SBill.Taylor@Sun.COM /* ARGSUSED */
22159517SBill.Taylor@Sun.COM int
hermon_wrid_from_reset_handling(hermon_state_t * state,hermon_qphdl_t qp)22169517SBill.Taylor@Sun.COM hermon_wrid_from_reset_handling(hermon_state_t *state, hermon_qphdl_t qp)
22179517SBill.Taylor@Sun.COM {
22189517SBill.Taylor@Sun.COM 	hermon_workq_hdr_t	*swq, *rwq;
22199517SBill.Taylor@Sun.COM 
2220*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_alloc_flags & IBT_QP_USER_MAP)
22219517SBill.Taylor@Sun.COM 		return (DDI_SUCCESS);
22229517SBill.Taylor@Sun.COM 
2223*12965SWilliam.Taylor@Oracle.COM #ifdef __lock_lint
22249517SBill.Taylor@Sun.COM 	mutex_enter(&qp->qp_rq_cqhdl->cq_lock);
22259517SBill.Taylor@Sun.COM 	mutex_enter(&qp->qp_sq_cqhdl->cq_lock);
22269517SBill.Taylor@Sun.COM #else
2227*12965SWilliam.Taylor@Oracle.COM 	/* grab the cq lock(s) to modify the wqavl tree */
2228*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_rq_cqhdl)
2229*12965SWilliam.Taylor@Oracle.COM 		mutex_enter(&qp->qp_rq_cqhdl->cq_lock);
2230*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_rq_cqhdl != qp->qp_sq_cqhdl &&
2231*12965SWilliam.Taylor@Oracle.COM 	    qp->qp_sq_cqhdl != NULL)
22329517SBill.Taylor@Sun.COM 		mutex_enter(&qp->qp_sq_cqhdl->cq_lock);
22339517SBill.Taylor@Sun.COM #endif
22349517SBill.Taylor@Sun.COM 
22359517SBill.Taylor@Sun.COM 	/* Chain the newly allocated work queue header to the CQ's list */
2236*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_sq_cqhdl)
2237*12965SWilliam.Taylor@Oracle.COM 		hermon_cq_workq_add(qp->qp_sq_cqhdl, &qp->qp_sq_wqavl);
22389517SBill.Taylor@Sun.COM 
22399517SBill.Taylor@Sun.COM 	swq = qp->qp_sq_wqhdr;
22409517SBill.Taylor@Sun.COM 	swq->wq_head = 0;
22419517SBill.Taylor@Sun.COM 	swq->wq_tail = 0;
22429517SBill.Taylor@Sun.COM 	swq->wq_full = 0;
22439517SBill.Taylor@Sun.COM 
22449517SBill.Taylor@Sun.COM 	/*
22459517SBill.Taylor@Sun.COM 	 * Now we repeat all the above operations for the receive work queue,
22469517SBill.Taylor@Sun.COM 	 * or shared receive work queue.
22479517SBill.Taylor@Sun.COM 	 *
22489517SBill.Taylor@Sun.COM 	 * Note: We still use the 'qp_rq_cqhdl' even in the SRQ case.
22499517SBill.Taylor@Sun.COM 	 */
22509517SBill.Taylor@Sun.COM 
22519517SBill.Taylor@Sun.COM #ifdef __lock_lint
22529517SBill.Taylor@Sun.COM 	mutex_enter(&qp->qp_srqhdl->srq_lock);
22539517SBill.Taylor@Sun.COM #else
2254*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_alloc_flags & IBT_QP_USES_SRQ) {
22559517SBill.Taylor@Sun.COM 		mutex_enter(&qp->qp_srqhdl->srq_lock);
22569517SBill.Taylor@Sun.COM 	} else {
22579517SBill.Taylor@Sun.COM 		rwq = qp->qp_rq_wqhdr;
22589517SBill.Taylor@Sun.COM 		rwq->wq_head = 0;
22599517SBill.Taylor@Sun.COM 		rwq->wq_tail = 0;
22609517SBill.Taylor@Sun.COM 		rwq->wq_full = 0;
22619517SBill.Taylor@Sun.COM 		qp->qp_rq_wqecntr = 0;
22629517SBill.Taylor@Sun.COM 	}
22639517SBill.Taylor@Sun.COM #endif
22649517SBill.Taylor@Sun.COM 	hermon_cq_workq_add(qp->qp_rq_cqhdl, &qp->qp_rq_wqavl);
22659517SBill.Taylor@Sun.COM 
22669517SBill.Taylor@Sun.COM #ifdef __lock_lint
22679517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_srqhdl->srq_lock);
22689517SBill.Taylor@Sun.COM #else
2269*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_alloc_flags & IBT_QP_USES_SRQ) {
22709517SBill.Taylor@Sun.COM 		mutex_exit(&qp->qp_srqhdl->srq_lock);
22719517SBill.Taylor@Sun.COM 	}
22729517SBill.Taylor@Sun.COM #endif
22739517SBill.Taylor@Sun.COM 
22749517SBill.Taylor@Sun.COM #ifdef __lock_lint
22759517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_sq_cqhdl->cq_lock);
2276*12965SWilliam.Taylor@Oracle.COM 	mutex_exit(&qp->qp_rq_cqhdl->cq_lock);
22779517SBill.Taylor@Sun.COM #else
2278*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_rq_cqhdl != qp->qp_sq_cqhdl &&
2279*12965SWilliam.Taylor@Oracle.COM 	    qp->qp_sq_cqhdl != NULL)
22809517SBill.Taylor@Sun.COM 		mutex_exit(&qp->qp_sq_cqhdl->cq_lock);
2281*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_rq_cqhdl)
2282*12965SWilliam.Taylor@Oracle.COM 		mutex_exit(&qp->qp_rq_cqhdl->cq_lock);
22839517SBill.Taylor@Sun.COM #endif
22849517SBill.Taylor@Sun.COM 	return (DDI_SUCCESS);
22859517SBill.Taylor@Sun.COM }
22869517SBill.Taylor@Sun.COM 
22879517SBill.Taylor@Sun.COM 
22889517SBill.Taylor@Sun.COM /*
22899517SBill.Taylor@Sun.COM  * hermon_wrid_to_reset_handling()
22909517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
22919517SBill.Taylor@Sun.COM  */
22929517SBill.Taylor@Sun.COM int
hermon_wrid_to_reset_handling(hermon_state_t * state,hermon_qphdl_t qp)22939517SBill.Taylor@Sun.COM hermon_wrid_to_reset_handling(hermon_state_t *state, hermon_qphdl_t qp)
22949517SBill.Taylor@Sun.COM {
2295*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_alloc_flags & IBT_QP_USER_MAP)
22969517SBill.Taylor@Sun.COM 		return (DDI_SUCCESS);
22979517SBill.Taylor@Sun.COM 
22989517SBill.Taylor@Sun.COM 	/*
22999517SBill.Taylor@Sun.COM 	 * If there are unpolled entries in these CQs, they are
23009517SBill.Taylor@Sun.COM 	 * polled/flushed.
23019517SBill.Taylor@Sun.COM 	 * Grab the CQ lock(s) before manipulating the lists.
23029517SBill.Taylor@Sun.COM 	 */
2303*12965SWilliam.Taylor@Oracle.COM #ifdef __lock_lint
23049517SBill.Taylor@Sun.COM 	mutex_enter(&qp->qp_rq_cqhdl->cq_lock);
23059517SBill.Taylor@Sun.COM 	mutex_enter(&qp->qp_sq_cqhdl->cq_lock);
23069517SBill.Taylor@Sun.COM #else
2307*12965SWilliam.Taylor@Oracle.COM 	/* grab the cq lock(s) to modify the wqavl tree */
2308*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_rq_cqhdl)
2309*12965SWilliam.Taylor@Oracle.COM 		mutex_enter(&qp->qp_rq_cqhdl->cq_lock);
2310*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_rq_cqhdl != qp->qp_sq_cqhdl &&
2311*12965SWilliam.Taylor@Oracle.COM 	    qp->qp_sq_cqhdl != NULL)
23129517SBill.Taylor@Sun.COM 		mutex_enter(&qp->qp_sq_cqhdl->cq_lock);
23139517SBill.Taylor@Sun.COM #endif
23149517SBill.Taylor@Sun.COM 
23159517SBill.Taylor@Sun.COM #ifdef __lock_lint
23169517SBill.Taylor@Sun.COM 	mutex_enter(&qp->qp_srqhdl->srq_lock);
23179517SBill.Taylor@Sun.COM #else
2318*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_alloc_flags & IBT_QP_USES_SRQ) {
23199517SBill.Taylor@Sun.COM 		mutex_enter(&qp->qp_srqhdl->srq_lock);
23209517SBill.Taylor@Sun.COM 	}
23219517SBill.Taylor@Sun.COM #endif
23229517SBill.Taylor@Sun.COM 	/*
23239517SBill.Taylor@Sun.COM 	 * Flush the entries on the CQ for this QP's QPN.
23249517SBill.Taylor@Sun.COM 	 */
23259517SBill.Taylor@Sun.COM 	hermon_cq_entries_flush(state, qp);
23269517SBill.Taylor@Sun.COM 
23279517SBill.Taylor@Sun.COM #ifdef __lock_lint
23289517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_srqhdl->srq_lock);
23299517SBill.Taylor@Sun.COM #else
2330*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_alloc_flags & IBT_QP_USES_SRQ) {
23319517SBill.Taylor@Sun.COM 		mutex_exit(&qp->qp_srqhdl->srq_lock);
23329517SBill.Taylor@Sun.COM 	}
23339517SBill.Taylor@Sun.COM #endif
23349517SBill.Taylor@Sun.COM 
23359517SBill.Taylor@Sun.COM 	hermon_cq_workq_remove(qp->qp_rq_cqhdl, &qp->qp_rq_wqavl);
2336*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_sq_cqhdl != NULL)
2337*12965SWilliam.Taylor@Oracle.COM 		hermon_cq_workq_remove(qp->qp_sq_cqhdl, &qp->qp_sq_wqavl);
23389517SBill.Taylor@Sun.COM 
23399517SBill.Taylor@Sun.COM #ifdef __lock_lint
23409517SBill.Taylor@Sun.COM 	mutex_exit(&qp->qp_sq_cqhdl->cq_lock);
2341*12965SWilliam.Taylor@Oracle.COM 	mutex_exit(&qp->qp_rq_cqhdl->cq_lock);
23429517SBill.Taylor@Sun.COM #else
2343*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_rq_cqhdl != qp->qp_sq_cqhdl &&
2344*12965SWilliam.Taylor@Oracle.COM 	    qp->qp_sq_cqhdl != NULL)
23459517SBill.Taylor@Sun.COM 		mutex_exit(&qp->qp_sq_cqhdl->cq_lock);
2346*12965SWilliam.Taylor@Oracle.COM 	if (qp->qp_rq_cqhdl)
2347*12965SWilliam.Taylor@Oracle.COM 		mutex_exit(&qp->qp_rq_cqhdl->cq_lock);
23489517SBill.Taylor@Sun.COM #endif
23499517SBill.Taylor@Sun.COM 
23509517SBill.Taylor@Sun.COM 	return (IBT_SUCCESS);
23519517SBill.Taylor@Sun.COM }
23529517SBill.Taylor@Sun.COM 
23539517SBill.Taylor@Sun.COM 
23549517SBill.Taylor@Sun.COM /*
23559517SBill.Taylor@Sun.COM  * hermon_wrid_get_entry()
23569517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
23579517SBill.Taylor@Sun.COM  */
23589517SBill.Taylor@Sun.COM uint64_t
hermon_wrid_get_entry(hermon_cqhdl_t cq,hermon_hw_cqe_t * cqe)23599517SBill.Taylor@Sun.COM hermon_wrid_get_entry(hermon_cqhdl_t cq, hermon_hw_cqe_t *cqe)
23609517SBill.Taylor@Sun.COM {
23619517SBill.Taylor@Sun.COM 	hermon_workq_avl_t	*wqa;
23629517SBill.Taylor@Sun.COM 	hermon_workq_hdr_t	*wq;
23639517SBill.Taylor@Sun.COM 	uint64_t		wrid;
23649517SBill.Taylor@Sun.COM 	uint_t			send_or_recv, qpnum;
23659517SBill.Taylor@Sun.COM 	uint32_t		indx;
23669517SBill.Taylor@Sun.COM 
23679517SBill.Taylor@Sun.COM 	/*
23689517SBill.Taylor@Sun.COM 	 * Determine whether this CQE is a send or receive completion.
23699517SBill.Taylor@Sun.COM 	 */
23709517SBill.Taylor@Sun.COM 	send_or_recv = HERMON_CQE_SENDRECV_GET(cq, cqe);
23719517SBill.Taylor@Sun.COM 
23729517SBill.Taylor@Sun.COM 	/* Find the work queue for this QP number (send or receive side) */
23739517SBill.Taylor@Sun.COM 	qpnum = HERMON_CQE_QPNUM_GET(cq, cqe);
23749517SBill.Taylor@Sun.COM 	wqa = hermon_wrid_wqavl_find(cq, qpnum, send_or_recv);
23759517SBill.Taylor@Sun.COM 	wq = wqa->wqa_wq;
23769517SBill.Taylor@Sun.COM 
23779517SBill.Taylor@Sun.COM 	/*
23789517SBill.Taylor@Sun.COM 	 * Regardless of whether the completion is the result of a "success"
23799517SBill.Taylor@Sun.COM 	 * or a "failure", we lock the list of "containers" and attempt to
23809517SBill.Taylor@Sun.COM 	 * search for the the first matching completion (i.e. the first WR
23819517SBill.Taylor@Sun.COM 	 * with a matching WQE addr and size).  Once we find it, we pull out
23829517SBill.Taylor@Sun.COM 	 * the "wrid" field and return it (see below).  XXX Note: One possible
23839517SBill.Taylor@Sun.COM 	 * future enhancement would be to enable this routine to skip over
23849517SBill.Taylor@Sun.COM 	 * any "unsignaled" completions to go directly to the next "signaled"
23859517SBill.Taylor@Sun.COM 	 * entry on success.
23869517SBill.Taylor@Sun.COM 	 */
23879517SBill.Taylor@Sun.COM 	indx = HERMON_CQE_WQEADDRSZ_GET(cq, cqe) & wq->wq_mask;
23889517SBill.Taylor@Sun.COM 	wrid = wq->wq_wrid[indx];
23899517SBill.Taylor@Sun.COM 	if (wqa->wqa_srq_en) {
23909517SBill.Taylor@Sun.COM 		struct hermon_sw_srq_s	*srq;
23919517SBill.Taylor@Sun.COM 		uint64_t		*desc;
23929517SBill.Taylor@Sun.COM 
23939517SBill.Taylor@Sun.COM 		/* put wqe back on the srq free list */
23949517SBill.Taylor@Sun.COM 		srq = wqa->wqa_srq;
23959517SBill.Taylor@Sun.COM 		mutex_enter(&srq->srq_lock);
23969517SBill.Taylor@Sun.COM 		desc = HERMON_SRQ_WQE_ADDR(srq, wq->wq_tail);
23979517SBill.Taylor@Sun.COM 		((uint16_t *)desc)[1] = htons(indx);
23989517SBill.Taylor@Sun.COM 		wq->wq_tail = indx;
23999517SBill.Taylor@Sun.COM 		mutex_exit(&srq->srq_lock);
24009517SBill.Taylor@Sun.COM 	} else {
24019517SBill.Taylor@Sun.COM 		wq->wq_head = (indx + 1) & wq->wq_mask;
24029517SBill.Taylor@Sun.COM 		wq->wq_full = 0;
24039517SBill.Taylor@Sun.COM 	}
24049517SBill.Taylor@Sun.COM 
24059517SBill.Taylor@Sun.COM 	return (wrid);
24069517SBill.Taylor@Sun.COM }
24079517SBill.Taylor@Sun.COM 
24089517SBill.Taylor@Sun.COM 
24099517SBill.Taylor@Sun.COM int
hermon_wrid_workq_compare(const void * p1,const void * p2)24109517SBill.Taylor@Sun.COM hermon_wrid_workq_compare(const void *p1, const void *p2)
24119517SBill.Taylor@Sun.COM {
24129517SBill.Taylor@Sun.COM 	hermon_workq_compare_t	*cmpp;
24139517SBill.Taylor@Sun.COM 	hermon_workq_avl_t	*curr;
24149517SBill.Taylor@Sun.COM 
24159517SBill.Taylor@Sun.COM 	cmpp = (hermon_workq_compare_t *)p1;
24169517SBill.Taylor@Sun.COM 	curr = (hermon_workq_avl_t *)p2;
24179517SBill.Taylor@Sun.COM 
24189517SBill.Taylor@Sun.COM 	if (cmpp->cmp_qpn < curr->wqa_qpn)
24199517SBill.Taylor@Sun.COM 		return (-1);
24209517SBill.Taylor@Sun.COM 	else if (cmpp->cmp_qpn > curr->wqa_qpn)
24219517SBill.Taylor@Sun.COM 		return (+1);
24229517SBill.Taylor@Sun.COM 	else if (cmpp->cmp_type < curr->wqa_type)
24239517SBill.Taylor@Sun.COM 		return (-1);
24249517SBill.Taylor@Sun.COM 	else if (cmpp->cmp_type > curr->wqa_type)
24259517SBill.Taylor@Sun.COM 		return (+1);
24269517SBill.Taylor@Sun.COM 	else
24279517SBill.Taylor@Sun.COM 		return (0);
24289517SBill.Taylor@Sun.COM }
24299517SBill.Taylor@Sun.COM 
24309517SBill.Taylor@Sun.COM 
24319517SBill.Taylor@Sun.COM /*
24329517SBill.Taylor@Sun.COM  * hermon_wrid_workq_find()
24339517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
24349517SBill.Taylor@Sun.COM  */
24359517SBill.Taylor@Sun.COM static hermon_workq_avl_t *
hermon_wrid_wqavl_find(hermon_cqhdl_t cq,uint_t qpn,uint_t wq_type)24369517SBill.Taylor@Sun.COM hermon_wrid_wqavl_find(hermon_cqhdl_t cq, uint_t qpn, uint_t wq_type)
24379517SBill.Taylor@Sun.COM {
24389517SBill.Taylor@Sun.COM 	hermon_workq_avl_t	*curr;
24399517SBill.Taylor@Sun.COM 	hermon_workq_compare_t	cmp;
24409517SBill.Taylor@Sun.COM 
24419517SBill.Taylor@Sun.COM 	/*
24429517SBill.Taylor@Sun.COM 	 * Walk the CQ's work queue list, trying to find a send or recv queue
24439517SBill.Taylor@Sun.COM 	 * with the same QP number.  We do this even if we are going to later
24449517SBill.Taylor@Sun.COM 	 * create a new entry because it helps us easily find the end of the
24459517SBill.Taylor@Sun.COM 	 * list.
24469517SBill.Taylor@Sun.COM 	 */
24479517SBill.Taylor@Sun.COM 	cmp.cmp_qpn = qpn;
24489517SBill.Taylor@Sun.COM 	cmp.cmp_type = wq_type;
24499517SBill.Taylor@Sun.COM #ifdef __lock_lint
24509517SBill.Taylor@Sun.COM 	hermon_wrid_workq_compare(NULL, NULL);
24519517SBill.Taylor@Sun.COM #endif
24529517SBill.Taylor@Sun.COM 	curr = avl_find(&cq->cq_wrid_wqhdr_avl_tree, &cmp, NULL);
24539517SBill.Taylor@Sun.COM 
24549517SBill.Taylor@Sun.COM 	return (curr);
24559517SBill.Taylor@Sun.COM }
24569517SBill.Taylor@Sun.COM 
24579517SBill.Taylor@Sun.COM 
24589517SBill.Taylor@Sun.COM /*
24599517SBill.Taylor@Sun.COM  * hermon_wrid_wqhdr_create()
24609517SBill.Taylor@Sun.COM  *    Context: Can be called from base context.
24619517SBill.Taylor@Sun.COM  */
24629517SBill.Taylor@Sun.COM /* ARGSUSED */
24639517SBill.Taylor@Sun.COM hermon_workq_hdr_t *
hermon_wrid_wqhdr_create(int bufsz)24649517SBill.Taylor@Sun.COM hermon_wrid_wqhdr_create(int bufsz)
24659517SBill.Taylor@Sun.COM {
24669517SBill.Taylor@Sun.COM 	hermon_workq_hdr_t	*wqhdr;
24679517SBill.Taylor@Sun.COM 
24689517SBill.Taylor@Sun.COM 	/*
24699517SBill.Taylor@Sun.COM 	 * Allocate space for the wqhdr, and an array to record all the wrids.
24709517SBill.Taylor@Sun.COM 	 */
24719517SBill.Taylor@Sun.COM 	wqhdr = (hermon_workq_hdr_t *)kmem_zalloc(sizeof (*wqhdr), KM_NOSLEEP);
24729517SBill.Taylor@Sun.COM 	if (wqhdr == NULL) {
24739517SBill.Taylor@Sun.COM 		return (NULL);
24749517SBill.Taylor@Sun.COM 	}
24759517SBill.Taylor@Sun.COM 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*wqhdr))
24769517SBill.Taylor@Sun.COM 	wqhdr->wq_wrid = kmem_zalloc(bufsz * sizeof (uint64_t), KM_NOSLEEP);
24779517SBill.Taylor@Sun.COM 	if (wqhdr->wq_wrid == NULL) {
24789517SBill.Taylor@Sun.COM 		kmem_free(wqhdr, sizeof (*wqhdr));
24799517SBill.Taylor@Sun.COM 		return (NULL);
24809517SBill.Taylor@Sun.COM 	}
24819517SBill.Taylor@Sun.COM 	wqhdr->wq_size = bufsz;
24829517SBill.Taylor@Sun.COM 	wqhdr->wq_mask = bufsz - 1;
24839517SBill.Taylor@Sun.COM 
24849517SBill.Taylor@Sun.COM 	return (wqhdr);
24859517SBill.Taylor@Sun.COM }
24869517SBill.Taylor@Sun.COM 
24879517SBill.Taylor@Sun.COM void
hermon_wrid_wqhdr_destroy(hermon_workq_hdr_t * wqhdr)24889517SBill.Taylor@Sun.COM hermon_wrid_wqhdr_destroy(hermon_workq_hdr_t *wqhdr)
24899517SBill.Taylor@Sun.COM {
24909517SBill.Taylor@Sun.COM 	kmem_free(wqhdr->wq_wrid, wqhdr->wq_size * sizeof (uint64_t));
24919517SBill.Taylor@Sun.COM 	kmem_free(wqhdr, sizeof (*wqhdr));
24929517SBill.Taylor@Sun.COM }
24939517SBill.Taylor@Sun.COM 
24949517SBill.Taylor@Sun.COM 
24959517SBill.Taylor@Sun.COM /*
24969517SBill.Taylor@Sun.COM  * hermon_cq_workq_add()
24979517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
24989517SBill.Taylor@Sun.COM  */
24999517SBill.Taylor@Sun.COM static void
hermon_cq_workq_add(hermon_cqhdl_t cq,hermon_workq_avl_t * wqavl)25009517SBill.Taylor@Sun.COM hermon_cq_workq_add(hermon_cqhdl_t cq, hermon_workq_avl_t *wqavl)
25019517SBill.Taylor@Sun.COM {
25029517SBill.Taylor@Sun.COM 	hermon_workq_compare_t	cmp;
25039517SBill.Taylor@Sun.COM 	avl_index_t		where;
25049517SBill.Taylor@Sun.COM 
25059517SBill.Taylor@Sun.COM 	cmp.cmp_qpn = wqavl->wqa_qpn;
25069517SBill.Taylor@Sun.COM 	cmp.cmp_type = wqavl->wqa_type;
25079517SBill.Taylor@Sun.COM #ifdef __lock_lint
25089517SBill.Taylor@Sun.COM 	hermon_wrid_workq_compare(NULL, NULL);
25099517SBill.Taylor@Sun.COM #endif
25109517SBill.Taylor@Sun.COM 	(void) avl_find(&cq->cq_wrid_wqhdr_avl_tree, &cmp, &where);
25119517SBill.Taylor@Sun.COM 	avl_insert(&cq->cq_wrid_wqhdr_avl_tree, wqavl, where);
25129517SBill.Taylor@Sun.COM }
25139517SBill.Taylor@Sun.COM 
25149517SBill.Taylor@Sun.COM 
25159517SBill.Taylor@Sun.COM /*
25169517SBill.Taylor@Sun.COM  * hermon_cq_workq_remove()
25179517SBill.Taylor@Sun.COM  *    Context: Can be called from interrupt or base context.
25189517SBill.Taylor@Sun.COM  */
25199517SBill.Taylor@Sun.COM static void
hermon_cq_workq_remove(hermon_cqhdl_t cq,hermon_workq_avl_t * wqavl)25209517SBill.Taylor@Sun.COM hermon_cq_workq_remove(hermon_cqhdl_t cq, hermon_workq_avl_t *wqavl)
25219517SBill.Taylor@Sun.COM {
25229517SBill.Taylor@Sun.COM #ifdef __lock_lint
25239517SBill.Taylor@Sun.COM 	hermon_wrid_workq_compare(NULL, NULL);
25249517SBill.Taylor@Sun.COM #endif
25259517SBill.Taylor@Sun.COM 	avl_remove(&cq->cq_wrid_wqhdr_avl_tree, wqavl);
25269517SBill.Taylor@Sun.COM }
2527