xref: /openbmc/linux/drivers/infiniband/hw/hfi1/ruc.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
1*145eba1aSCai Huoqing // SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
2f48ad614SDennis Dalessandro /*
32e2ba09eSMike Marciniszyn  * Copyright(c) 2015 - 2018 Intel Corporation.
4f48ad614SDennis Dalessandro  */
5f48ad614SDennis Dalessandro 
6f48ad614SDennis Dalessandro #include <linux/spinlock.h>
7f48ad614SDennis Dalessandro 
8f48ad614SDennis Dalessandro #include "hfi.h"
9f48ad614SDennis Dalessandro #include "mad.h"
10f48ad614SDennis Dalessandro #include "qp.h"
11f48ad614SDennis Dalessandro #include "verbs_txreq.h"
12f48ad614SDennis Dalessandro #include "trace.h"
13f48ad614SDennis Dalessandro 
gid_ok(union ib_gid * gid,__be64 gid_prefix,__be64 id)14f48ad614SDennis Dalessandro static int gid_ok(union ib_gid *gid, __be64 gid_prefix, __be64 id)
15f48ad614SDennis Dalessandro {
16f48ad614SDennis Dalessandro 	return (gid->global.interface_id == id &&
17f48ad614SDennis Dalessandro 		(gid->global.subnet_prefix == gid_prefix ||
18f48ad614SDennis Dalessandro 		 gid->global.subnet_prefix == IB_DEFAULT_GID_PREFIX));
19f48ad614SDennis Dalessandro }
20f48ad614SDennis Dalessandro 
21f48ad614SDennis Dalessandro /*
22f48ad614SDennis Dalessandro  *
23f48ad614SDennis Dalessandro  * This should be called with the QP r_lock held.
24f48ad614SDennis Dalessandro  *
25f48ad614SDennis Dalessandro  * The s_lock will be acquired around the hfi1_migrate_qp() call.
26f48ad614SDennis Dalessandro  */
hfi1_ruc_check_hdr(struct hfi1_ibport * ibp,struct hfi1_packet * packet)279039746cSDon Hiatt int hfi1_ruc_check_hdr(struct hfi1_ibport *ibp, struct hfi1_packet *packet)
28f48ad614SDennis Dalessandro {
29f48ad614SDennis Dalessandro 	__be64 guid;
30f48ad614SDennis Dalessandro 	unsigned long flags;
319039746cSDon Hiatt 	struct rvt_qp *qp = packet->qp;
32d8966fcdSDasaratharaman Chandramouli 	u8 sc5 = ibp->sl_to_sc[rdma_ah_get_sl(&qp->remote_ah_attr)];
339039746cSDon Hiatt 	u32 dlid = packet->dlid;
349039746cSDon Hiatt 	u32 slid = packet->slid;
359039746cSDon Hiatt 	u32 sl = packet->sl;
366d6b8848SSebastian Sanchez 	bool migrated = packet->migrated;
376d6b8848SSebastian Sanchez 	u16 pkey = packet->pkey;
389039746cSDon Hiatt 
399039746cSDon Hiatt 	if (qp->s_mig_state == IB_MIG_ARMED && migrated) {
409039746cSDon Hiatt 		if (!packet->grh) {
415786adf3SDon Hiatt 			if ((rdma_ah_get_ah_flags(&qp->alt_ah_attr) &
425786adf3SDon Hiatt 			     IB_AH_GRH) &&
435786adf3SDon Hiatt 			    (packet->etype != RHF_RCV_TYPE_BYPASS))
449039746cSDon Hiatt 				return 1;
45f48ad614SDennis Dalessandro 		} else {
46d8966fcdSDasaratharaman Chandramouli 			const struct ib_global_route *grh;
47d8966fcdSDasaratharaman Chandramouli 
48d8966fcdSDasaratharaman Chandramouli 			if (!(rdma_ah_get_ah_flags(&qp->alt_ah_attr) &
49d8966fcdSDasaratharaman Chandramouli 			      IB_AH_GRH))
509039746cSDon Hiatt 				return 1;
51d8966fcdSDasaratharaman Chandramouli 			grh = rdma_ah_read_grh(&qp->alt_ah_attr);
52d8966fcdSDasaratharaman Chandramouli 			guid = get_sguid(ibp, grh->sgid_index);
539039746cSDon Hiatt 			if (!gid_ok(&packet->grh->dgid, ibp->rvp.gid_prefix,
54f48ad614SDennis Dalessandro 				    guid))
559039746cSDon Hiatt 				return 1;
56f48ad614SDennis Dalessandro 			if (!gid_ok(
579039746cSDon Hiatt 				&packet->grh->sgid,
58d8966fcdSDasaratharaman Chandramouli 				grh->dgid.global.subnet_prefix,
59d8966fcdSDasaratharaman Chandramouli 				grh->dgid.global.interface_id))
609039746cSDon Hiatt 				return 1;
61f48ad614SDennis Dalessandro 		}
625786adf3SDon Hiatt 		if (unlikely(rcv_pkey_check(ppd_from_ibp(ibp), pkey,
639039746cSDon Hiatt 					    sc5, slid))) {
645786adf3SDon Hiatt 			hfi1_bad_pkey(ibp, pkey, sl, 0, qp->ibqp.qp_num,
655786adf3SDon Hiatt 				      slid, dlid);
669039746cSDon Hiatt 			return 1;
67f48ad614SDennis Dalessandro 		}
68f48ad614SDennis Dalessandro 		/* Validate the SLID. See Ch. 9.6.1.5 and 17.2.8 */
699039746cSDon Hiatt 		if (slid != rdma_ah_get_dlid(&qp->alt_ah_attr) ||
70d8966fcdSDasaratharaman Chandramouli 		    ppd_from_ibp(ibp)->port !=
71d8966fcdSDasaratharaman Chandramouli 			rdma_ah_get_port_num(&qp->alt_ah_attr))
729039746cSDon Hiatt 			return 1;
73f48ad614SDennis Dalessandro 		spin_lock_irqsave(&qp->s_lock, flags);
74f48ad614SDennis Dalessandro 		hfi1_migrate_qp(qp);
75f48ad614SDennis Dalessandro 		spin_unlock_irqrestore(&qp->s_lock, flags);
76f48ad614SDennis Dalessandro 	} else {
779039746cSDon Hiatt 		if (!packet->grh) {
785786adf3SDon Hiatt 			if ((rdma_ah_get_ah_flags(&qp->remote_ah_attr) &
795786adf3SDon Hiatt 			     IB_AH_GRH) &&
805786adf3SDon Hiatt 			    (packet->etype != RHF_RCV_TYPE_BYPASS))
819039746cSDon Hiatt 				return 1;
82f48ad614SDennis Dalessandro 		} else {
83d8966fcdSDasaratharaman Chandramouli 			const struct ib_global_route *grh;
84d8966fcdSDasaratharaman Chandramouli 
85d8966fcdSDasaratharaman Chandramouli 			if (!(rdma_ah_get_ah_flags(&qp->remote_ah_attr) &
86d8966fcdSDasaratharaman Chandramouli 						   IB_AH_GRH))
879039746cSDon Hiatt 				return 1;
88d8966fcdSDasaratharaman Chandramouli 			grh = rdma_ah_read_grh(&qp->remote_ah_attr);
89d8966fcdSDasaratharaman Chandramouli 			guid = get_sguid(ibp, grh->sgid_index);
909039746cSDon Hiatt 			if (!gid_ok(&packet->grh->dgid, ibp->rvp.gid_prefix,
91f48ad614SDennis Dalessandro 				    guid))
929039746cSDon Hiatt 				return 1;
93f48ad614SDennis Dalessandro 			if (!gid_ok(
949039746cSDon Hiatt 			     &packet->grh->sgid,
95d8966fcdSDasaratharaman Chandramouli 			     grh->dgid.global.subnet_prefix,
96d8966fcdSDasaratharaman Chandramouli 			     grh->dgid.global.interface_id))
979039746cSDon Hiatt 				return 1;
98f48ad614SDennis Dalessandro 		}
995786adf3SDon Hiatt 		if (unlikely(rcv_pkey_check(ppd_from_ibp(ibp), pkey,
1009039746cSDon Hiatt 					    sc5, slid))) {
1015786adf3SDon Hiatt 			hfi1_bad_pkey(ibp, pkey, sl, 0, qp->ibqp.qp_num,
1025786adf3SDon Hiatt 				      slid, dlid);
1039039746cSDon Hiatt 			return 1;
104f48ad614SDennis Dalessandro 		}
105f48ad614SDennis Dalessandro 		/* Validate the SLID. See Ch. 9.6.1.5 */
1069039746cSDon Hiatt 		if ((slid != rdma_ah_get_dlid(&qp->remote_ah_attr)) ||
107f48ad614SDennis Dalessandro 		    ppd_from_ibp(ibp)->port != qp->port_num)
1089039746cSDon Hiatt 			return 1;
1099039746cSDon Hiatt 		if (qp->s_mig_state == IB_MIG_REARM && !migrated)
110f48ad614SDennis Dalessandro 			qp->s_mig_state = IB_MIG_ARMED;
111f48ad614SDennis Dalessandro 	}
112f48ad614SDennis Dalessandro 
113f48ad614SDennis Dalessandro 	return 0;
114f48ad614SDennis Dalessandro }
115f48ad614SDennis Dalessandro 
116f48ad614SDennis Dalessandro /**
117f48ad614SDennis Dalessandro  * hfi1_make_grh - construct a GRH header
118f48ad614SDennis Dalessandro  * @ibp: a pointer to the IB port
119f48ad614SDennis Dalessandro  * @hdr: a pointer to the GRH header being constructed
120f48ad614SDennis Dalessandro  * @grh: the global route address to send to
12188733e3bSDon Hiatt  * @hwords: size of header after grh being sent in dwords
122f48ad614SDennis Dalessandro  * @nwords: the number of 32 bit words of data being sent
123f48ad614SDennis Dalessandro  *
124f48ad614SDennis Dalessandro  * Return the size of the header in 32 bit words.
125f48ad614SDennis Dalessandro  */
hfi1_make_grh(struct hfi1_ibport * ibp,struct ib_grh * hdr,const struct ib_global_route * grh,u32 hwords,u32 nwords)126f48ad614SDennis Dalessandro u32 hfi1_make_grh(struct hfi1_ibport *ibp, struct ib_grh *hdr,
127d8966fcdSDasaratharaman Chandramouli 		  const struct ib_global_route *grh, u32 hwords, u32 nwords)
128f48ad614SDennis Dalessandro {
129f48ad614SDennis Dalessandro 	hdr->version_tclass_flow =
130f48ad614SDennis Dalessandro 		cpu_to_be32((IB_GRH_VERSION << IB_GRH_VERSION_SHIFT) |
131f48ad614SDennis Dalessandro 			    (grh->traffic_class << IB_GRH_TCLASS_SHIFT) |
132f48ad614SDennis Dalessandro 			    (grh->flow_label << IB_GRH_FLOW_SHIFT));
13388733e3bSDon Hiatt 	hdr->paylen = cpu_to_be16((hwords + nwords) << 2);
134f48ad614SDennis Dalessandro 	/* next_hdr is defined by C8-7 in ch. 8.4.1 */
135f48ad614SDennis Dalessandro 	hdr->next_hdr = IB_GRH_NEXT_HDR;
136f48ad614SDennis Dalessandro 	hdr->hop_limit = grh->hop_limit;
137f48ad614SDennis Dalessandro 	/* The SGID is 32-bit aligned. */
138f48ad614SDennis Dalessandro 	hdr->sgid.global.subnet_prefix = ibp->rvp.gid_prefix;
139f48ad614SDennis Dalessandro 	hdr->sgid.global.interface_id =
140a6cd5f08SJakub Pawlak 		grh->sgid_index < HFI1_GUIDS_PER_PORT ?
141a6cd5f08SJakub Pawlak 		get_sguid(ibp, grh->sgid_index) :
142a6cd5f08SJakub Pawlak 		get_sguid(ibp, HFI1_PORT_GUID_INDEX);
143f48ad614SDennis Dalessandro 	hdr->dgid = grh->dgid;
144f48ad614SDennis Dalessandro 
145f48ad614SDennis Dalessandro 	/* GRH header size in 32-bit words. */
146f48ad614SDennis Dalessandro 	return sizeof(struct ib_grh) / sizeof(u32);
147f48ad614SDennis Dalessandro }
148f48ad614SDennis Dalessandro 
14930e07416SDon Hiatt #define BTH2_OFFSET (offsetof(struct hfi1_sdma_header, \
15030e07416SDon Hiatt 			      hdr.ibh.u.oth.bth[2]) / 4)
151f48ad614SDennis Dalessandro 
152f48ad614SDennis Dalessandro /**
153a9b6b3bcSDasaratharaman Chandramouli  * build_ahg - create ahg in s_ahg
154f48ad614SDennis Dalessandro  * @qp: a pointer to QP
155f48ad614SDennis Dalessandro  * @npsn: the next PSN for the request/response
156f48ad614SDennis Dalessandro  *
157f48ad614SDennis Dalessandro  * This routine handles the AHG by allocating an ahg entry and causing the
158f48ad614SDennis Dalessandro  * copy of the first middle.
159f48ad614SDennis Dalessandro  *
160f48ad614SDennis Dalessandro  * Subsequent middles use the copied entry, editing the
161f48ad614SDennis Dalessandro  * PSN with 1 or 2 edits.
162f48ad614SDennis Dalessandro  */
build_ahg(struct rvt_qp * qp,u32 npsn)163f48ad614SDennis Dalessandro static inline void build_ahg(struct rvt_qp *qp, u32 npsn)
164f48ad614SDennis Dalessandro {
165f48ad614SDennis Dalessandro 	struct hfi1_qp_priv *priv = qp->priv;
166f48ad614SDennis Dalessandro 
1672e2ba09eSMike Marciniszyn 	if (unlikely(qp->s_flags & HFI1_S_AHG_CLEAR))
168f48ad614SDennis Dalessandro 		clear_ahg(qp);
1692e2ba09eSMike Marciniszyn 	if (!(qp->s_flags & HFI1_S_AHG_VALID)) {
170f48ad614SDennis Dalessandro 		/* first middle that needs copy  */
171f48ad614SDennis Dalessandro 		if (qp->s_ahgidx < 0)
172f48ad614SDennis Dalessandro 			qp->s_ahgidx = sdma_ahg_alloc(priv->s_sde);
173f48ad614SDennis Dalessandro 		if (qp->s_ahgidx >= 0) {
174f48ad614SDennis Dalessandro 			qp->s_ahgpsn = npsn;
175a9b6b3bcSDasaratharaman Chandramouli 			priv->s_ahg->tx_flags |= SDMA_TXREQ_F_AHG_COPY;
176f48ad614SDennis Dalessandro 			/* save to protect a change in another thread */
177a9b6b3bcSDasaratharaman Chandramouli 			priv->s_ahg->ahgidx = qp->s_ahgidx;
1782e2ba09eSMike Marciniszyn 			qp->s_flags |= HFI1_S_AHG_VALID;
179f48ad614SDennis Dalessandro 		}
180f48ad614SDennis Dalessandro 	} else {
181f48ad614SDennis Dalessandro 		/* subsequent middle after valid */
182f48ad614SDennis Dalessandro 		if (qp->s_ahgidx >= 0) {
183a9b6b3bcSDasaratharaman Chandramouli 			priv->s_ahg->tx_flags |= SDMA_TXREQ_F_USE_AHG;
184a9b6b3bcSDasaratharaman Chandramouli 			priv->s_ahg->ahgidx = qp->s_ahgidx;
185a9b6b3bcSDasaratharaman Chandramouli 			priv->s_ahg->ahgcount++;
186a9b6b3bcSDasaratharaman Chandramouli 			priv->s_ahg->ahgdesc[0] =
187f48ad614SDennis Dalessandro 				sdma_build_ahg_descriptor(
188f48ad614SDennis Dalessandro 					(__force u16)cpu_to_be16((u16)npsn),
189f48ad614SDennis Dalessandro 					BTH2_OFFSET,
190f48ad614SDennis Dalessandro 					16,
191f48ad614SDennis Dalessandro 					16);
192f48ad614SDennis Dalessandro 			if ((npsn & 0xffff0000) !=
193f48ad614SDennis Dalessandro 					(qp->s_ahgpsn & 0xffff0000)) {
194a9b6b3bcSDasaratharaman Chandramouli 				priv->s_ahg->ahgcount++;
195a9b6b3bcSDasaratharaman Chandramouli 				priv->s_ahg->ahgdesc[1] =
196f48ad614SDennis Dalessandro 					sdma_build_ahg_descriptor(
197f48ad614SDennis Dalessandro 						(__force u16)cpu_to_be16(
198f48ad614SDennis Dalessandro 							(u16)(npsn >> 16)),
199f48ad614SDennis Dalessandro 						BTH2_OFFSET,
200f48ad614SDennis Dalessandro 						0,
201f48ad614SDennis Dalessandro 						16);
202f48ad614SDennis Dalessandro 			}
203f48ad614SDennis Dalessandro 		}
204f48ad614SDennis Dalessandro 	}
205f48ad614SDennis Dalessandro }
206f48ad614SDennis Dalessandro 
hfi1_make_ruc_bth(struct rvt_qp * qp,struct ib_other_headers * ohdr,u32 bth0,u32 bth1,u32 bth2)2075b6cabb0SDon Hiatt static inline void hfi1_make_ruc_bth(struct rvt_qp *qp,
2085b6cabb0SDon Hiatt 				     struct ib_other_headers *ohdr,
2095b6cabb0SDon Hiatt 				     u32 bth0, u32 bth1, u32 bth2)
2105b6cabb0SDon Hiatt {
2115b6cabb0SDon Hiatt 	ohdr->bth[0] = cpu_to_be32(bth0);
2125b6cabb0SDon Hiatt 	ohdr->bth[1] = cpu_to_be32(bth1);
2135b6cabb0SDon Hiatt 	ohdr->bth[2] = cpu_to_be32(bth2);
2145b6cabb0SDon Hiatt }
2155b6cabb0SDon Hiatt 
2160a0bcb04SMike Marciniszyn /**
2170a0bcb04SMike Marciniszyn  * hfi1_make_ruc_header_16B - build a 16B header
2180a0bcb04SMike Marciniszyn  * @qp: the queue pair
2190a0bcb04SMike Marciniszyn  * @ohdr: a pointer to the destination header memory
2200a0bcb04SMike Marciniszyn  * @bth0: bth0 passed in from the RC/UC builder
2210654a746SLee Jones  * @bth1: bth1 passed in from the RC/UC builder
2220a0bcb04SMike Marciniszyn  * @bth2: bth2 passed in from the RC/UC builder
2230a0bcb04SMike Marciniszyn  * @middle: non zero implies indicates ahg "could" be used
2240a0bcb04SMike Marciniszyn  * @ps: the current packet state
2250a0bcb04SMike Marciniszyn  *
2260a0bcb04SMike Marciniszyn  * This routine may disarm ahg under these situations:
2270a0bcb04SMike Marciniszyn  * - packet needs a GRH
2280a0bcb04SMike Marciniszyn  * - BECN needed
2290a0bcb04SMike Marciniszyn  * - migration state not IB_MIG_MIGRATED
2300a0bcb04SMike Marciniszyn  */
hfi1_make_ruc_header_16B(struct rvt_qp * qp,struct ib_other_headers * ohdr,u32 bth0,u32 bth1,u32 bth2,int middle,struct hfi1_pkt_state * ps)2315b6cabb0SDon Hiatt static inline void hfi1_make_ruc_header_16B(struct rvt_qp *qp,
2325b6cabb0SDon Hiatt 					    struct ib_other_headers *ohdr,
23344e43d91SMitko Haralanov 					    u32 bth0, u32 bth1, u32 bth2,
23444e43d91SMitko Haralanov 					    int middle,
235f48ad614SDennis Dalessandro 					    struct hfi1_pkt_state *ps)
236f48ad614SDennis Dalessandro {
237f48ad614SDennis Dalessandro 	struct hfi1_qp_priv *priv = qp->priv;
238f48ad614SDennis Dalessandro 	struct hfi1_ibport *ibp = ps->ibp;
2395b6cabb0SDon Hiatt 	struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
2405b6cabb0SDon Hiatt 	u32 slid;
2415b6cabb0SDon Hiatt 	u16 pkey = hfi1_get_pkey(ibp, qp->s_pkey_index);
2425b6cabb0SDon Hiatt 	u8 l4 = OPA_16B_L4_IB_LOCAL;
2439636258fSMitko Haralanov 	u8 extra_bytes = hfi1_get_16b_padding(
2449636258fSMitko Haralanov 				(ps->s_txreq->hdr_dwords << 2),
2455b6cabb0SDon Hiatt 				ps->s_txreq->s_cur_size);
2465b6cabb0SDon Hiatt 	u32 nwords = SIZE_OF_CRC + ((ps->s_txreq->s_cur_size +
2475b6cabb0SDon Hiatt 				 extra_bytes + SIZE_OF_LT) >> 2);
248ca85bb1cSSebastian Sanchez 	bool becn = false;
249f48ad614SDennis Dalessandro 
2505b6cabb0SDon Hiatt 	if (unlikely(rdma_ah_get_ah_flags(&qp->remote_ah_attr) & IB_AH_GRH) &&
2515b6cabb0SDon Hiatt 	    hfi1_check_mcast(rdma_ah_get_dlid(&qp->remote_ah_attr))) {
2525b6cabb0SDon Hiatt 		struct ib_grh *grh;
2535b6cabb0SDon Hiatt 		struct ib_global_route *grd =
2545b6cabb0SDon Hiatt 			rdma_ah_retrieve_grh(&qp->remote_ah_attr);
2555b6cabb0SDon Hiatt 		/*
2565b6cabb0SDon Hiatt 		 * Ensure OPA GIDs are transformed to IB gids
2575b6cabb0SDon Hiatt 		 * before creating the GRH.
2585b6cabb0SDon Hiatt 		 */
2595b6cabb0SDon Hiatt 		if (grd->sgid_index == OPA_GID_INDEX)
2605b6cabb0SDon Hiatt 			grd->sgid_index = 0;
2615b6cabb0SDon Hiatt 		grh = &ps->s_txreq->phdr.hdr.opah.u.l.grh;
2625b6cabb0SDon Hiatt 		l4 = OPA_16B_L4_IB_GLOBAL;
26378d3633bSMike Marciniszyn 		ps->s_txreq->hdr_dwords +=
26478d3633bSMike Marciniszyn 			hfi1_make_grh(ibp, grh, grd,
26578d3633bSMike Marciniszyn 				      ps->s_txreq->hdr_dwords - LRH_16B_DWORDS,
26678d3633bSMike Marciniszyn 				      nwords);
2675b6cabb0SDon Hiatt 		middle = 0;
2685b6cabb0SDon Hiatt 	}
2695b6cabb0SDon Hiatt 
2705b6cabb0SDon Hiatt 	if (qp->s_mig_state == IB_MIG_MIGRATED)
2715b6cabb0SDon Hiatt 		bth1 |= OPA_BTH_MIG_REQ;
2725b6cabb0SDon Hiatt 	else
2735b6cabb0SDon Hiatt 		middle = 0;
2745b6cabb0SDon Hiatt 
2750a0bcb04SMike Marciniszyn 	if (qp->s_flags & RVT_S_ECN) {
2760a0bcb04SMike Marciniszyn 		qp->s_flags &= ~RVT_S_ECN;
2770a0bcb04SMike Marciniszyn 		/* we recently received a FECN, so return a BECN */
2780a0bcb04SMike Marciniszyn 		becn = true;
2790a0bcb04SMike Marciniszyn 		middle = 0;
2800a0bcb04SMike Marciniszyn 	}
2815b6cabb0SDon Hiatt 	if (middle)
2825b6cabb0SDon Hiatt 		build_ahg(qp, bth2);
2835b6cabb0SDon Hiatt 	else
2842e2ba09eSMike Marciniszyn 		qp->s_flags &= ~HFI1_S_AHG_VALID;
2855b6cabb0SDon Hiatt 
2865b6cabb0SDon Hiatt 	bth0 |= pkey;
2875b6cabb0SDon Hiatt 	bth0 |= extra_bytes << 20;
2885b6cabb0SDon Hiatt 	hfi1_make_ruc_bth(qp, ohdr, bth0, bth1, bth2);
2895b6cabb0SDon Hiatt 
2905b6cabb0SDon Hiatt 	if (!ppd->lid)
2915b6cabb0SDon Hiatt 		slid = be32_to_cpu(OPA_LID_PERMISSIVE);
2925b6cabb0SDon Hiatt 	else
2935b6cabb0SDon Hiatt 		slid = ppd->lid |
2945b6cabb0SDon Hiatt 			(rdma_ah_get_path_bits(&qp->remote_ah_attr) &
2955b6cabb0SDon Hiatt 			((1 << ppd->lmc) - 1));
2965b6cabb0SDon Hiatt 
2975b6cabb0SDon Hiatt 	hfi1_make_16b_hdr(&ps->s_txreq->phdr.hdr.opah,
2985b6cabb0SDon Hiatt 			  slid,
2995b6cabb0SDon Hiatt 			  opa_get_lid(rdma_ah_get_dlid(&qp->remote_ah_attr),
3005b6cabb0SDon Hiatt 				      16B),
3019636258fSMitko Haralanov 			  (ps->s_txreq->hdr_dwords + nwords) >> 1,
3025b6cabb0SDon Hiatt 			  pkey, becn, 0, l4, priv->s_sc);
3035b6cabb0SDon Hiatt }
3045b6cabb0SDon Hiatt 
3050a0bcb04SMike Marciniszyn /**
3060a0bcb04SMike Marciniszyn  * hfi1_make_ruc_header_9B - build a 9B header
3070a0bcb04SMike Marciniszyn  * @qp: the queue pair
3080a0bcb04SMike Marciniszyn  * @ohdr: a pointer to the destination header memory
3090a0bcb04SMike Marciniszyn  * @bth0: bth0 passed in from the RC/UC builder
3100654a746SLee Jones  * @bth1: bth1 passed in from the RC/UC builder
3110a0bcb04SMike Marciniszyn  * @bth2: bth2 passed in from the RC/UC builder
3120a0bcb04SMike Marciniszyn  * @middle: non zero implies indicates ahg "could" be used
3130a0bcb04SMike Marciniszyn  * @ps: the current packet state
3140a0bcb04SMike Marciniszyn  *
3150a0bcb04SMike Marciniszyn  * This routine may disarm ahg under these situations:
3160a0bcb04SMike Marciniszyn  * - packet needs a GRH
3170a0bcb04SMike Marciniszyn  * - BECN needed
3180a0bcb04SMike Marciniszyn  * - migration state not IB_MIG_MIGRATED
3190a0bcb04SMike Marciniszyn  */
hfi1_make_ruc_header_9B(struct rvt_qp * qp,struct ib_other_headers * ohdr,u32 bth0,u32 bth1,u32 bth2,int middle,struct hfi1_pkt_state * ps)3205b6cabb0SDon Hiatt static inline void hfi1_make_ruc_header_9B(struct rvt_qp *qp,
3215b6cabb0SDon Hiatt 					   struct ib_other_headers *ohdr,
32244e43d91SMitko Haralanov 					   u32 bth0, u32 bth1, u32 bth2,
32344e43d91SMitko Haralanov 					   int middle,
3245b6cabb0SDon Hiatt 					   struct hfi1_pkt_state *ps)
3255b6cabb0SDon Hiatt {
3265b6cabb0SDon Hiatt 	struct hfi1_qp_priv *priv = qp->priv;
3275b6cabb0SDon Hiatt 	struct hfi1_ibport *ibp = ps->ibp;
3285b6cabb0SDon Hiatt 	u16 pkey = hfi1_get_pkey(ibp, qp->s_pkey_index);
3295b6cabb0SDon Hiatt 	u16 lrh0 = HFI1_LRH_BTH;
3305b6cabb0SDon Hiatt 	u8 extra_bytes = -ps->s_txreq->s_cur_size & 3;
3315b6cabb0SDon Hiatt 	u32 nwords = SIZE_OF_CRC + ((ps->s_txreq->s_cur_size +
3325b6cabb0SDon Hiatt 					 extra_bytes) >> 2);
3335b6cabb0SDon Hiatt 
334d8966fcdSDasaratharaman Chandramouli 	if (unlikely(rdma_ah_get_ah_flags(&qp->remote_ah_attr) & IB_AH_GRH)) {
3355b6cabb0SDon Hiatt 		struct ib_grh *grh = &ps->s_txreq->phdr.hdr.ibh.u.l.grh;
3365b6cabb0SDon Hiatt 
337f48ad614SDennis Dalessandro 		lrh0 = HFI1_LRH_GRH;
3389636258fSMitko Haralanov 		ps->s_txreq->hdr_dwords +=
3395b6cabb0SDon Hiatt 			hfi1_make_grh(ibp, grh,
3405b6cabb0SDon Hiatt 				      rdma_ah_read_grh(&qp->remote_ah_attr),
34178d3633bSMike Marciniszyn 				      ps->s_txreq->hdr_dwords - LRH_9B_DWORDS,
34278d3633bSMike Marciniszyn 				      nwords);
343f48ad614SDennis Dalessandro 		middle = 0;
344f48ad614SDennis Dalessandro 	}
345d8966fcdSDasaratharaman Chandramouli 	lrh0 |= (priv->s_sc & 0xf) << 12 |
346d8966fcdSDasaratharaman Chandramouli 		(rdma_ah_get_sl(&qp->remote_ah_attr) & 0xf) << 4;
3475b6cabb0SDon Hiatt 
3485b6cabb0SDon Hiatt 	if (qp->s_mig_state == IB_MIG_MIGRATED)
3495b6cabb0SDon Hiatt 		bth0 |= IB_BTH_MIG_REQ;
3505b6cabb0SDon Hiatt 	else
3515b6cabb0SDon Hiatt 		middle = 0;
3525b6cabb0SDon Hiatt 
3530a0bcb04SMike Marciniszyn 	if (qp->s_flags & RVT_S_ECN) {
3540a0bcb04SMike Marciniszyn 		qp->s_flags &= ~RVT_S_ECN;
3550a0bcb04SMike Marciniszyn 		/* we recently received a FECN, so return a BECN */
3560a0bcb04SMike Marciniszyn 		bth1 |= (IB_BECN_MASK << IB_BECN_SHIFT);
3570a0bcb04SMike Marciniszyn 		middle = 0;
3580a0bcb04SMike Marciniszyn 	}
3595b6cabb0SDon Hiatt 	if (middle)
3605b6cabb0SDon Hiatt 		build_ahg(qp, bth2);
3615b6cabb0SDon Hiatt 	else
3622e2ba09eSMike Marciniszyn 		qp->s_flags &= ~HFI1_S_AHG_VALID;
3635b6cabb0SDon Hiatt 
3645b6cabb0SDon Hiatt 	bth0 |= pkey;
3655b6cabb0SDon Hiatt 	bth0 |= extra_bytes << 20;
3665b6cabb0SDon Hiatt 	hfi1_make_ruc_bth(qp, ohdr, bth0, bth1, bth2);
3675b6cabb0SDon Hiatt 	hfi1_make_ib_hdr(&ps->s_txreq->phdr.hdr.ibh,
3685b6cabb0SDon Hiatt 			 lrh0,
3699636258fSMitko Haralanov 			 ps->s_txreq->hdr_dwords + nwords,
3705b6cabb0SDon Hiatt 			 opa_get_lid(rdma_ah_get_dlid(&qp->remote_ah_attr), 9B),
3715b6cabb0SDon Hiatt 			 ppd_from_ibp(ibp)->lid |
3725b6cabb0SDon Hiatt 				rdma_ah_get_path_bits(&qp->remote_ah_attr));
3735b6cabb0SDon Hiatt }
3745b6cabb0SDon Hiatt 
3755b6cabb0SDon Hiatt typedef void (*hfi1_make_ruc_hdr)(struct rvt_qp *qp,
3765b6cabb0SDon Hiatt 				  struct ib_other_headers *ohdr,
37744e43d91SMitko Haralanov 				  u32 bth0, u32 bth1, u32 bth2, int middle,
3785b6cabb0SDon Hiatt 				  struct hfi1_pkt_state *ps);
3795b6cabb0SDon Hiatt 
3805b6cabb0SDon Hiatt /* We support only two types - 9B and 16B for now */
3815b6cabb0SDon Hiatt static const hfi1_make_ruc_hdr hfi1_ruc_header_tbl[2] = {
3825b6cabb0SDon Hiatt 	[HFI1_PKT_TYPE_9B] = &hfi1_make_ruc_header_9B,
3835b6cabb0SDon Hiatt 	[HFI1_PKT_TYPE_16B] = &hfi1_make_ruc_header_16B
3845b6cabb0SDon Hiatt };
3855b6cabb0SDon Hiatt 
hfi1_make_ruc_header(struct rvt_qp * qp,struct ib_other_headers * ohdr,u32 bth0,u32 bth1,u32 bth2,int middle,struct hfi1_pkt_state * ps)3865b6cabb0SDon Hiatt void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr,
38744e43d91SMitko Haralanov 			  u32 bth0, u32 bth1, u32 bth2, int middle,
3885b6cabb0SDon Hiatt 			  struct hfi1_pkt_state *ps)
3895b6cabb0SDon Hiatt {
3905b6cabb0SDon Hiatt 	struct hfi1_qp_priv *priv = qp->priv;
3915b6cabb0SDon Hiatt 
392f48ad614SDennis Dalessandro 	/*
393a9b6b3bcSDasaratharaman Chandramouli 	 * reset s_ahg/AHG fields
394f48ad614SDennis Dalessandro 	 *
395f48ad614SDennis Dalessandro 	 * This insures that the ahgentry/ahgcount
396f48ad614SDennis Dalessandro 	 * are at a non-AHG default to protect
397f48ad614SDennis Dalessandro 	 * build_verbs_tx_desc() from using
398f48ad614SDennis Dalessandro 	 * an include ahgidx.
399f48ad614SDennis Dalessandro 	 *
400f48ad614SDennis Dalessandro 	 * build_ahg() will modify as appropriate
401f48ad614SDennis Dalessandro 	 * to use the AHG feature.
402f48ad614SDennis Dalessandro 	 */
403a9b6b3bcSDasaratharaman Chandramouli 	priv->s_ahg->tx_flags = 0;
404a9b6b3bcSDasaratharaman Chandramouli 	priv->s_ahg->ahgcount = 0;
405a9b6b3bcSDasaratharaman Chandramouli 	priv->s_ahg->ahgidx = 0;
4065b6cabb0SDon Hiatt 
4075b6cabb0SDon Hiatt 	/* Make the appropriate header */
40844e43d91SMitko Haralanov 	hfi1_ruc_header_tbl[priv->hdr_type](qp, ohdr, bth0, bth1, bth2, middle,
40944e43d91SMitko Haralanov 					    ps);
410f48ad614SDennis Dalessandro }
411f48ad614SDennis Dalessandro 
412f48ad614SDennis Dalessandro /* when sending, force a reschedule every one of these periods */
413f48ad614SDennis Dalessandro #define SEND_RESCHED_TIMEOUT (5 * HZ)  /* 5s in jiffies */
414f48ad614SDennis Dalessandro 
415dd1ed108SMike Marciniszyn /**
416572f0c33SKaike Wan  * hfi1_schedule_send_yield - test for a yield required for QP
417572f0c33SKaike Wan  * send engine
418dd1ed108SMike Marciniszyn  * @qp: a pointer to QP
419dd1ed108SMike Marciniszyn  * @ps: a pointer to a structure with commonly lookup values for
420991c4274SCai Huoqing  *      the send engine progress
4210654a746SLee Jones  * @tid: true if it is the tid leg
422dd1ed108SMike Marciniszyn  *
423dd1ed108SMike Marciniszyn  * This routine checks if the time slice for the QP has expired
424dd1ed108SMike Marciniszyn  * for RC QPs, if so an additional work entry is queued. At this
425dd1ed108SMike Marciniszyn  * point, other QPs have an opportunity to be scheduled. It
426dd1ed108SMike Marciniszyn  * returns true if a yield is required, otherwise, false
427dd1ed108SMike Marciniszyn  * is returned.
428dd1ed108SMike Marciniszyn  */
hfi1_schedule_send_yield(struct rvt_qp * qp,struct hfi1_pkt_state * ps,bool tid)429572f0c33SKaike Wan bool hfi1_schedule_send_yield(struct rvt_qp *qp, struct hfi1_pkt_state *ps,
430572f0c33SKaike Wan 			      bool tid)
431dd1ed108SMike Marciniszyn {
432bcad2913SKaike Wan 	ps->pkts_sent = true;
433bcad2913SKaike Wan 
434dd1ed108SMike Marciniszyn 	if (unlikely(time_after(jiffies, ps->timeout))) {
435dd1ed108SMike Marciniszyn 		if (!ps->in_thread ||
436dd1ed108SMike Marciniszyn 		    workqueue_congested(ps->cpu, ps->ppd->hfi1_wq)) {
437dd1ed108SMike Marciniszyn 			spin_lock_irqsave(&qp->s_lock, ps->flags);
438572f0c33SKaike Wan 			if (!tid) {
439dd1ed108SMike Marciniszyn 				qp->s_flags &= ~RVT_S_BUSY;
440dd1ed108SMike Marciniszyn 				hfi1_schedule_send(qp);
441572f0c33SKaike Wan 			} else {
442572f0c33SKaike Wan 				struct hfi1_qp_priv *priv = qp->priv;
443572f0c33SKaike Wan 
444572f0c33SKaike Wan 				if (priv->s_flags &
445572f0c33SKaike Wan 				    HFI1_S_TID_BUSY_SET) {
446572f0c33SKaike Wan 					qp->s_flags &= ~RVT_S_BUSY;
447572f0c33SKaike Wan 					priv->s_flags &=
448572f0c33SKaike Wan 						~(HFI1_S_TID_BUSY_SET |
449572f0c33SKaike Wan 						  RVT_S_BUSY);
450572f0c33SKaike Wan 				} else {
451572f0c33SKaike Wan 					priv->s_flags &= ~RVT_S_BUSY;
452572f0c33SKaike Wan 				}
453572f0c33SKaike Wan 				hfi1_schedule_tid_send(qp);
454572f0c33SKaike Wan 			}
455572f0c33SKaike Wan 
456dd1ed108SMike Marciniszyn 			spin_unlock_irqrestore(&qp->s_lock, ps->flags);
457dd1ed108SMike Marciniszyn 			this_cpu_inc(*ps->ppd->dd->send_schedule);
458dd1ed108SMike Marciniszyn 			trace_hfi1_rc_expired_time_slice(qp, true);
459dd1ed108SMike Marciniszyn 			return true;
460dd1ed108SMike Marciniszyn 		}
461dd1ed108SMike Marciniszyn 
462dd1ed108SMike Marciniszyn 		cond_resched();
463dd1ed108SMike Marciniszyn 		this_cpu_inc(*ps->ppd->dd->send_schedule);
464dd1ed108SMike Marciniszyn 		ps->timeout = jiffies + ps->timeout_int;
465dd1ed108SMike Marciniszyn 	}
466dd1ed108SMike Marciniszyn 
467dd1ed108SMike Marciniszyn 	trace_hfi1_rc_expired_time_slice(qp, false);
468dd1ed108SMike Marciniszyn 	return false;
469dd1ed108SMike Marciniszyn }
470dd1ed108SMike Marciniszyn 
hfi1_do_send_from_rvt(struct rvt_qp * qp)471b6eac931SMike Marciniszyn void hfi1_do_send_from_rvt(struct rvt_qp *qp)
472b6eac931SMike Marciniszyn {
473b6eac931SMike Marciniszyn 	hfi1_do_send(qp, false);
474b6eac931SMike Marciniszyn }
475b6eac931SMike Marciniszyn 
_hfi1_do_send(struct work_struct * work)476f48ad614SDennis Dalessandro void _hfi1_do_send(struct work_struct *work)
477f48ad614SDennis Dalessandro {
4785da0fc9dSDennis Dalessandro 	struct iowait_work *w = container_of(work, struct iowait_work, iowork);
4795da0fc9dSDennis Dalessandro 	struct rvt_qp *qp = iowait_to_qp(w->iow);
480f48ad614SDennis Dalessandro 
481b6eac931SMike Marciniszyn 	hfi1_do_send(qp, true);
482f48ad614SDennis Dalessandro }
483f48ad614SDennis Dalessandro 
484f48ad614SDennis Dalessandro /**
485f48ad614SDennis Dalessandro  * hfi1_do_send - perform a send on a QP
486ea752bc5SKaike Wan  * @qp: a pointer to the QP
487b6eac931SMike Marciniszyn  * @in_thread: true if in a workqueue thread
488f48ad614SDennis Dalessandro  *
489f48ad614SDennis Dalessandro  * Process entries in the send work queue until credit or queue is
490ca00c62bSDennis Dalessandro  * exhausted.  Only allow one CPU to send a packet per QP.
491f48ad614SDennis Dalessandro  * Otherwise, two threads could send packets out of order.
492f48ad614SDennis Dalessandro  */
hfi1_do_send(struct rvt_qp * qp,bool in_thread)493b6eac931SMike Marciniszyn void hfi1_do_send(struct rvt_qp *qp, bool in_thread)
494f48ad614SDennis Dalessandro {
495f48ad614SDennis Dalessandro 	struct hfi1_pkt_state ps;
496f48ad614SDennis Dalessandro 	struct hfi1_qp_priv *priv = qp->priv;
497f48ad614SDennis Dalessandro 	int (*make_req)(struct rvt_qp *qp, struct hfi1_pkt_state *ps);
498f48ad614SDennis Dalessandro 
499f48ad614SDennis Dalessandro 	ps.dev = to_idev(qp->ibqp.device);
500f48ad614SDennis Dalessandro 	ps.ibp = to_iport(qp->ibqp.device, qp->port_num);
501f48ad614SDennis Dalessandro 	ps.ppd = ppd_from_ibp(ps.ibp);
502dd1ed108SMike Marciniszyn 	ps.in_thread = in_thread;
5035da0fc9dSDennis Dalessandro 	ps.wait = iowait_get_ib_work(&priv->s_iowait);
504dd1ed108SMike Marciniszyn 
505dd1ed108SMike Marciniszyn 	trace_hfi1_rc_do_send(qp, in_thread);
506f48ad614SDennis Dalessandro 
507f48ad614SDennis Dalessandro 	switch (qp->ibqp.qp_type) {
508f48ad614SDennis Dalessandro 	case IB_QPT_RC:
509d8966fcdSDasaratharaman Chandramouli 		if (!loopback && ((rdma_ah_get_dlid(&qp->remote_ah_attr) &
510d8966fcdSDasaratharaman Chandramouli 				   ~((1 << ps.ppd->lmc) - 1)) ==
511f48ad614SDennis Dalessandro 				  ps.ppd->lid)) {
51215703461SVenkata Sandeep Dhanalakota 			rvt_ruc_loopback(qp);
513f48ad614SDennis Dalessandro 			return;
514f48ad614SDennis Dalessandro 		}
515f48ad614SDennis Dalessandro 		make_req = hfi1_make_rc_req;
516dd1ed108SMike Marciniszyn 		ps.timeout_int = qp->timeout_jiffies;
517f48ad614SDennis Dalessandro 		break;
518f48ad614SDennis Dalessandro 	case IB_QPT_UC:
519d8966fcdSDasaratharaman Chandramouli 		if (!loopback && ((rdma_ah_get_dlid(&qp->remote_ah_attr) &
520d8966fcdSDasaratharaman Chandramouli 				   ~((1 << ps.ppd->lmc) - 1)) ==
521f48ad614SDennis Dalessandro 				  ps.ppd->lid)) {
52215703461SVenkata Sandeep Dhanalakota 			rvt_ruc_loopback(qp);
523f48ad614SDennis Dalessandro 			return;
524f48ad614SDennis Dalessandro 		}
525f48ad614SDennis Dalessandro 		make_req = hfi1_make_uc_req;
526dd1ed108SMike Marciniszyn 		ps.timeout_int = SEND_RESCHED_TIMEOUT;
527f48ad614SDennis Dalessandro 		break;
528f48ad614SDennis Dalessandro 	default:
529f48ad614SDennis Dalessandro 		make_req = hfi1_make_ud_req;
530dd1ed108SMike Marciniszyn 		ps.timeout_int = SEND_RESCHED_TIMEOUT;
531f48ad614SDennis Dalessandro 	}
532f48ad614SDennis Dalessandro 
533f48ad614SDennis Dalessandro 	spin_lock_irqsave(&qp->s_lock, ps.flags);
534f48ad614SDennis Dalessandro 
535f48ad614SDennis Dalessandro 	/* Return if we are already busy processing a work request. */
536f48ad614SDennis Dalessandro 	if (!hfi1_send_ok(qp)) {
5375da0fc9dSDennis Dalessandro 		if (qp->s_flags & HFI1_S_ANY_WAIT_IO)
5385da0fc9dSDennis Dalessandro 			iowait_set_flag(&priv->s_iowait, IOWAIT_PENDING_IB);
539f48ad614SDennis Dalessandro 		spin_unlock_irqrestore(&qp->s_lock, ps.flags);
540f48ad614SDennis Dalessandro 		return;
541f48ad614SDennis Dalessandro 	}
542f48ad614SDennis Dalessandro 
543f48ad614SDennis Dalessandro 	qp->s_flags |= RVT_S_BUSY;
544f48ad614SDennis Dalessandro 
545dd1ed108SMike Marciniszyn 	ps.timeout_int = ps.timeout_int / 8;
546dd1ed108SMike Marciniszyn 	ps.timeout = jiffies + ps.timeout_int;
547dd1ed108SMike Marciniszyn 	ps.cpu = priv->s_sde ? priv->s_sde->cpu :
548f48ad614SDennis Dalessandro 			cpumask_first(cpumask_of_node(ps.ppd->dd->node));
549bcad2913SKaike Wan 	ps.pkts_sent = false;
550dd1ed108SMike Marciniszyn 
551f48ad614SDennis Dalessandro 	/* insure a pre-built packet is handled  */
5525da0fc9dSDennis Dalessandro 	ps.s_txreq = get_waiting_verbs_txreq(ps.wait);
553f48ad614SDennis Dalessandro 	do {
554f48ad614SDennis Dalessandro 		/* Check for a constructed packet to be sent. */
5559636258fSMitko Haralanov 		if (ps.s_txreq) {
556572f0c33SKaike Wan 			if (priv->s_flags & HFI1_S_TID_BUSY_SET)
557572f0c33SKaike Wan 				qp->s_flags |= RVT_S_BUSY;
558f48ad614SDennis Dalessandro 			spin_unlock_irqrestore(&qp->s_lock, ps.flags);
559f48ad614SDennis Dalessandro 			/*
560f48ad614SDennis Dalessandro 			 * If the packet cannot be sent now, return and
561ca00c62bSDennis Dalessandro 			 * the send engine will be woken up later.
562f48ad614SDennis Dalessandro 			 */
563f48ad614SDennis Dalessandro 			if (hfi1_verbs_send(qp, &ps))
564f48ad614SDennis Dalessandro 				return;
5655da0fc9dSDennis Dalessandro 
566f48ad614SDennis Dalessandro 			/* allow other tasks to run */
567572f0c33SKaike Wan 			if (hfi1_schedule_send_yield(qp, &ps, false))
568f48ad614SDennis Dalessandro 				return;
569dd1ed108SMike Marciniszyn 
570f48ad614SDennis Dalessandro 			spin_lock_irqsave(&qp->s_lock, ps.flags);
571f48ad614SDennis Dalessandro 		}
572f48ad614SDennis Dalessandro 	} while (make_req(qp, &ps));
573bcad2913SKaike Wan 	iowait_starve_clear(ps.pkts_sent, &priv->s_iowait);
574f48ad614SDennis Dalessandro 	spin_unlock_irqrestore(&qp->s_lock, ps.flags);
575f48ad614SDennis Dalessandro }
576