// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright(c) 2015 - 2019 Intel Corporation.
*/
#include <linux/net.h>
#include <rdma/ib_smi.h>
#include "hfi.h"
#include "mad.h"
#include "verbs_txreq.h"
#include "trace_ibhdrs.h"
#include "qp.h"
/* We support only two types - 9B and 16B for now */
static const hfi1_make_req hfi1_make_ud_req_tbl[2] = {
[HFI1_PKT_TYPE_9B] = &hfi1_make_ud_req_9B,
[HFI1_PKT_TYPE_16B] = &hfi1_make_ud_req_16B
};
/**
* ud_loopback - handle send on loopback QPs
* @sqp: the sending QP
* @swqe: the send work request
*
* This is called from hfi1_make_ud_req() to forward a WQE addressed
* to the same HFI.
* Note that the receive interrupt handler may be calling hfi1_ud_rcv()
* while this is being called.
*/
static void ud_loopback(struct rvt_qp *sqp, struct rvt_swqe *swqe)
{
struct hfi1_ibport *ibp = to_iport(sqp->ibqp.device, sqp->port_num);
struct hfi1_pportdata *ppd;
struct hfi1_qp_priv *priv = sqp->priv;
struct rvt_qp *qp;
struct rdma_ah_attr *ah_attr;
unsigned long flags;
struct rvt_sge_state ssge;
struct rvt_sge *sge;
struct ib_wc wc;
u32 length;
enum ib_qp_type sqptype, dqptype;
rcu_read_lock();
qp = rvt_lookup_qpn(ib_to_rvt(sqp->ibqp.device), &ibp->rvp,
rvt_get_swqe_remote_qpn(swqe));
if (!qp) {
ibp->rvp.n_pkt_drops++;
rcu_read_unlock();
return;
}
sqptype = sqp->ibqp.qp_type == IB_QPT_GSI ?
IB_QPT_UD : sqp->ibqp.qp_type;
dqptype = qp->ibqp.qp_type == IB_QPT_GSI ?
IB_QPT_UD : qp->ibqp.qp_type;
if (dqptype != sqptype ||
!(ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK)) {
ibp->rvp.n_pkt_drops++;
goto drop;
}
ah_attr = rvt_get_swqe_ah_attr(swqe);
ppd = ppd_from_ibp(ibp);
if (qp->ibqp.qp_num > 1) {
u16 pkey;
u32 slid;
u8 sc5 = ibp->sl_to_sc[rdma_ah_get_sl(ah_attr)];
pkey = hfi1_get_pkey(ibp, sqp->s_pkey_index);
slid = ppd->lid | (rdma_ah_get_path_bits(ah_attr) &
((1 << ppd->lmc) - 1));
if (unlikely(ingress_pkey_check(ppd, pkey, sc5,
qp->s_pkey_index,
slid, false))) {
hfi1_bad_pkey(ibp, pkey,
rdma_ah_get_sl(ah_attr),
sqp->ibqp.qp_num, qp->ibqp.qp_num,
slid, rdma_ah_get_dlid(ah_attr));
goto drop;
}
}
/*
* Check that the qkey matches (except for QP0, see 9.6.1.4.1).
* Qkeys with the high order bit set mean use the
* qkey from the QP context instead of the WR (see 10.2.5).
*/
if (qp->ibqp.qp_num) {
u32 qkey;
qkey = (int)rvt_get_swqe_remote_qkey(swqe) < 0 ?
sqp->qkey : rvt_get_swqe_remote_qkey(swqe);
if (unlikely(qkey != qp->qkey))
goto drop; /* silently drop per IBTA spec */
}
/*
* A GRH is expected to precede the data even if not
* present on the wire.
*/
length = swqe->length;
memset(&wc, 0, sizeof(wc));
wc.byte_len = length + sizeof(struct ib_grh);
if (swqe->wr.opcode == IB_WR_SEND_WITH_IMM) {
wc.wc_flags = IB_WC_WITH_IMM;
wc.ex.imm_data = swqe->wr.ex.imm_data;
}
spin_lock_irqsave(&qp->r_lock, flags);
/*
* Get the next work request entry to find where to put the data.
*/
if (qp->r_flags & RVT_R_REUSE_SGE) {
qp->r_flags &= ~RVT_R_REUSE_SGE;
} else {
int ret;
ret = rvt_get_rwqe(qp, false);
if (ret < 0) {
rvt_rc_error(qp, IB_WC_LOC_QP_OP_ERR);
goto bail_unlock;
}
if (!ret) {
if (qp->ibqp.qp_num == 0)
ibp->rvp.n_vl15_dropped++;
goto bail_unlock;
}
}
/* Silently drop packets which are too big. */
if (unlikely(wc.byte_len > qp->r_len)) {
qp->r_flags |= RVT_R_REUSE_SGE;
ibp->rvp.n_pkt_drops++;
goto bail_unlock;
}
if (rdma_ah_get_ah_flags(ah_attr) & IB_AH_GRH) {
struct ib_grh grh;
struct ib_global_route grd = *(rdma_ah_read_grh(ah_attr));
/*
* For loopback packets with extended LIDs, the
* sgid_index in the GRH is 0 and the dgid is
* OPA GID of the sender. While creating a response
* to the loopback packet, IB core creates the new
* sgid_index from the DGID and that will be the
* OPA_GID_INDEX. The new dgid is from the sgid
* index and that will be in the IB GID format.
*
* We now have a case where the sent packet had a
* different sgid_index and dgid compared to the
* one that was received in response.
*
* Fix this inconsistency.
*/
if (priv->hdr_type == HFI1_PKT_TYPE_16B) {
if (grd.sgid_index == 0)
grd.sgid_index = OPA_GID_INDEX;
if (ib_is_opa_gid(&grd.dgid))
grd.dgid.global.interface_id =
cpu_to_be64(ppd->guids[HFI1_PORT_GUID_INDEX]);
}
hfi1_make_grh(ibp, &grh, &grd, 0, 0);
rvt_copy_sge(qp, &qp->r_sge, &grh,
sizeof(grh), true, false);
wc.wc_flags |= IB_WC_GRH;
} else {
rvt_skip_sge(&qp->r_sge, size