0f39cf3d54
Add a new IB_WR_SEND_WITH_INV send opcode that can be used to mark a "send with invalidate" work request as defined in the iWARP verbs and the InfiniBand base memory management extensions. Also put "imm_data" and a new "invalidate_rkey" member in a new "ex" union in struct ib_send_wr. The invalidate_rkey member can be used to pass in an R_Key/STag to be invalidated. Add this new union to struct ib_uverbs_send_wr. Add code to copy the invalidate_rkey field in ib_uverbs_post_send(). Fix up low-level drivers to deal with the change to struct ib_send_wr, and just remove the imm_data initialization from net/sunrpc/xprtrdma/, since that code never does any send with immediate operations. Also, move the existing IB_DEVICE_SEND_W_INV flag to a new bit, since the iWARP drivers currently in the tree set the bit. The amso1100 driver at least will silently fail to honor the IB_SEND_INVALIDATE bit if passed in as part of userspace send requests (since it does not implement kernel bypass work request queueing). Remove the flag from all existing drivers that set it until we know which ones are OK. The values chosen for the new flag is not consecutive to avoid clashing with flags defined in the XRC patches, which are not merged yet but which are already in use and are likely to be merged soon. This resurrects a patch sent long ago by Mikkel Hagen <mhagen@iol.unh.edu>. Signed-off-by: Roland Dreier <rolandd@cisco.com>
528 lines
13 KiB
C
528 lines
13 KiB
C
/*
|
|
* Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved.
|
|
* Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved.
|
|
*
|
|
* This software is available to you under a choice of one of two
|
|
* licenses. You may choose to be licensed under the terms of the GNU
|
|
* General Public License (GPL) Version 2, available from the file
|
|
* COPYING in the main directory of this source tree, or the
|
|
* OpenIB.org BSD license below:
|
|
*
|
|
* Redistribution and use in source and binary forms, with or
|
|
* without modification, are permitted provided that the following
|
|
* conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer.
|
|
*
|
|
* - Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials
|
|
* provided with the distribution.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "ipath_verbs.h"
|
|
#include "ipath_kernel.h"
|
|
|
|
/* cut down ridiculously long IB macro names */
|
|
#define OP(x) IB_OPCODE_UC_##x
|
|
|
|
/**
|
|
* ipath_make_uc_req - construct a request packet (SEND, RDMA write)
|
|
* @qp: a pointer to the QP
|
|
*
|
|
* Return 1 if constructed; otherwise, return 0.
|
|
*/
|
|
int ipath_make_uc_req(struct ipath_qp *qp)
|
|
{
|
|
struct ipath_other_headers *ohdr;
|
|
struct ipath_swqe *wqe;
|
|
u32 hwords;
|
|
u32 bth0;
|
|
u32 len;
|
|
u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
|
|
int ret = 0;
|
|
|
|
if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK))
|
|
goto done;
|
|
|
|
ohdr = &qp->s_hdr.u.oth;
|
|
if (qp->remote_ah_attr.ah_flags & IB_AH_GRH)
|
|
ohdr = &qp->s_hdr.u.l.oth;
|
|
|
|
/* header size in 32-bit words LRH+BTH = (8+12)/4. */
|
|
hwords = 5;
|
|
bth0 = 1 << 22; /* Set M bit */
|
|
|
|
/* Get the next send request. */
|
|
wqe = get_swqe_ptr(qp, qp->s_cur);
|
|
qp->s_wqe = NULL;
|
|
switch (qp->s_state) {
|
|
default:
|
|
/* Check if send work queue is empty. */
|
|
if (qp->s_cur == qp->s_head)
|
|
goto done;
|
|
/*
|
|
* Start a new request.
|
|
*/
|
|
qp->s_psn = wqe->psn = qp->s_next_psn;
|
|
qp->s_sge.sge = wqe->sg_list[0];
|
|
qp->s_sge.sg_list = wqe->sg_list + 1;
|
|
qp->s_sge.num_sge = wqe->wr.num_sge;
|
|
qp->s_len = len = wqe->length;
|
|
switch (wqe->wr.opcode) {
|
|
case IB_WR_SEND:
|
|
case IB_WR_SEND_WITH_IMM:
|
|
if (len > pmtu) {
|
|
qp->s_state = OP(SEND_FIRST);
|
|
len = pmtu;
|
|
break;
|
|
}
|
|
if (wqe->wr.opcode == IB_WR_SEND)
|
|
qp->s_state = OP(SEND_ONLY);
|
|
else {
|
|
qp->s_state =
|
|
OP(SEND_ONLY_WITH_IMMEDIATE);
|
|
/* Immediate data comes after the BTH */
|
|
ohdr->u.imm_data = wqe->wr.ex.imm_data;
|
|
hwords += 1;
|
|
}
|
|
if (wqe->wr.send_flags & IB_SEND_SOLICITED)
|
|
bth0 |= 1 << 23;
|
|
qp->s_wqe = wqe;
|
|
if (++qp->s_cur >= qp->s_size)
|
|
qp->s_cur = 0;
|
|
break;
|
|
|
|
case IB_WR_RDMA_WRITE:
|
|
case IB_WR_RDMA_WRITE_WITH_IMM:
|
|
ohdr->u.rc.reth.vaddr =
|
|
cpu_to_be64(wqe->wr.wr.rdma.remote_addr);
|
|
ohdr->u.rc.reth.rkey =
|
|
cpu_to_be32(wqe->wr.wr.rdma.rkey);
|
|
ohdr->u.rc.reth.length = cpu_to_be32(len);
|
|
hwords += sizeof(struct ib_reth) / 4;
|
|
if (len > pmtu) {
|
|
qp->s_state = OP(RDMA_WRITE_FIRST);
|
|
len = pmtu;
|
|
break;
|
|
}
|
|
if (wqe->wr.opcode == IB_WR_RDMA_WRITE)
|
|
qp->s_state = OP(RDMA_WRITE_ONLY);
|
|
else {
|
|
qp->s_state =
|
|
OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE);
|
|
/* Immediate data comes after the RETH */
|
|
ohdr->u.rc.imm_data = wqe->wr.ex.imm_data;
|
|
hwords += 1;
|
|
if (wqe->wr.send_flags & IB_SEND_SOLICITED)
|
|
bth0 |= 1 << 23;
|
|
}
|
|
qp->s_wqe = wqe;
|
|
if (++qp->s_cur >= qp->s_size)
|
|
qp->s_cur = 0;
|
|
break;
|
|
|
|
default:
|
|
goto done;
|
|
}
|
|
break;
|
|
|
|
case OP(SEND_FIRST):
|
|
qp->s_state = OP(SEND_MIDDLE);
|
|
/* FALLTHROUGH */
|
|
case OP(SEND_MIDDLE):
|
|
len = qp->s_len;
|
|
if (len > pmtu) {
|
|
len = pmtu;
|
|
break;
|
|
}
|
|
if (wqe->wr.opcode == IB_WR_SEND)
|
|
qp->s_state = OP(SEND_LAST);
|
|
else {
|
|
qp->s_state = OP(SEND_LAST_WITH_IMMEDIATE);
|
|
/* Immediate data comes after the BTH */
|
|
ohdr->u.imm_data = wqe->wr.ex.imm_data;
|
|
hwords += 1;
|
|
}
|
|
if (wqe->wr.send_flags & IB_SEND_SOLICITED)
|
|
bth0 |= 1 << 23;
|
|
qp->s_wqe = wqe;
|
|
if (++qp->s_cur >= qp->s_size)
|
|
qp->s_cur = 0;
|
|
break;
|
|
|
|
case OP(RDMA_WRITE_FIRST):
|
|
qp->s_state = OP(RDMA_WRITE_MIDDLE);
|
|
/* FALLTHROUGH */
|
|
case OP(RDMA_WRITE_MIDDLE):
|
|
len = qp->s_len;
|
|
if (len > pmtu) {
|
|
len = pmtu;
|
|
break;
|
|
}
|
|
if (wqe->wr.opcode == IB_WR_RDMA_WRITE)
|
|
qp->s_state = OP(RDMA_WRITE_LAST);
|
|
else {
|
|
qp->s_state =
|
|
OP(RDMA_WRITE_LAST_WITH_IMMEDIATE);
|
|
/* Immediate data comes after the BTH */
|
|
ohdr->u.imm_data = wqe->wr.ex.imm_data;
|
|
hwords += 1;
|
|
if (wqe->wr.send_flags & IB_SEND_SOLICITED)
|
|
bth0 |= 1 << 23;
|
|
}
|
|
qp->s_wqe = wqe;
|
|
if (++qp->s_cur >= qp->s_size)
|
|
qp->s_cur = 0;
|
|
break;
|
|
}
|
|
qp->s_len -= len;
|
|
qp->s_hdrwords = hwords;
|
|
qp->s_cur_sge = &qp->s_sge;
|
|
qp->s_cur_size = len;
|
|
ipath_make_ruc_header(to_idev(qp->ibqp.device),
|
|
qp, ohdr, bth0 | (qp->s_state << 24),
|
|
qp->s_next_psn++ & IPATH_PSN_MASK);
|
|
ret = 1;
|
|
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* ipath_uc_rcv - handle an incoming UC packet
|
|
* @dev: the device the packet came in on
|
|
* @hdr: the header of the packet
|
|
* @has_grh: true if the packet has a GRH
|
|
* @data: the packet data
|
|
* @tlen: the length of the packet
|
|
* @qp: the QP for this packet.
|
|
*
|
|
* This is called from ipath_qp_rcv() to process an incoming UC packet
|
|
* for the given QP.
|
|
* Called at interrupt level.
|
|
*/
|
|
void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
|
|
int has_grh, void *data, u32 tlen, struct ipath_qp *qp)
|
|
{
|
|
struct ipath_other_headers *ohdr;
|
|
int opcode;
|
|
u32 hdrsize;
|
|
u32 psn;
|
|
u32 pad;
|
|
struct ib_wc wc;
|
|
u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu);
|
|
struct ib_reth *reth;
|
|
int header_in_data;
|
|
|
|
/* Validate the SLID. See Ch. 9.6.1.5 */
|
|
if (unlikely(be16_to_cpu(hdr->lrh[3]) != qp->remote_ah_attr.dlid))
|
|
goto done;
|
|
|
|
/* Check for GRH */
|
|
if (!has_grh) {
|
|
ohdr = &hdr->u.oth;
|
|
hdrsize = 8 + 12; /* LRH + BTH */
|
|
psn = be32_to_cpu(ohdr->bth[2]);
|
|
header_in_data = 0;
|
|
} else {
|
|
ohdr = &hdr->u.l.oth;
|
|
hdrsize = 8 + 40 + 12; /* LRH + GRH + BTH */
|
|
/*
|
|
* The header with GRH is 60 bytes and the
|
|
* core driver sets the eager header buffer
|
|
* size to 56 bytes so the last 4 bytes of
|
|
* the BTH header (PSN) is in the data buffer.
|
|
*/
|
|
header_in_data = dev->dd->ipath_rcvhdrentsize == 16;
|
|
if (header_in_data) {
|
|
psn = be32_to_cpu(((__be32 *) data)[0]);
|
|
data += sizeof(__be32);
|
|
} else
|
|
psn = be32_to_cpu(ohdr->bth[2]);
|
|
}
|
|
/*
|
|
* The opcode is in the low byte when its in network order
|
|
* (top byte when in host order).
|
|
*/
|
|
opcode = be32_to_cpu(ohdr->bth[0]) >> 24;
|
|
|
|
wc.imm_data = 0;
|
|
wc.wc_flags = 0;
|
|
|
|
/* Compare the PSN verses the expected PSN. */
|
|
if (unlikely(ipath_cmp24(psn, qp->r_psn) != 0)) {
|
|
/*
|
|
* Handle a sequence error.
|
|
* Silently drop any current message.
|
|
*/
|
|
qp->r_psn = psn;
|
|
inv:
|
|
qp->r_state = OP(SEND_LAST);
|
|
switch (opcode) {
|
|
case OP(SEND_FIRST):
|
|
case OP(SEND_ONLY):
|
|
case OP(SEND_ONLY_WITH_IMMEDIATE):
|
|
goto send_first;
|
|
|
|
case OP(RDMA_WRITE_FIRST):
|
|
case OP(RDMA_WRITE_ONLY):
|
|
case OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE):
|
|
goto rdma_first;
|
|
|
|
default:
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Check for opcode sequence errors. */
|
|
switch (qp->r_state) {
|
|
case OP(SEND_FIRST):
|
|
case OP(SEND_MIDDLE):
|
|
if (opcode == OP(SEND_MIDDLE) ||
|
|
opcode == OP(SEND_LAST) ||
|
|
opcode == OP(SEND_LAST_WITH_IMMEDIATE))
|
|
break;
|
|
goto inv;
|
|
|
|
case OP(RDMA_WRITE_FIRST):
|
|
case OP(RDMA_WRITE_MIDDLE):
|
|
if (opcode == OP(RDMA_WRITE_MIDDLE) ||
|
|
opcode == OP(RDMA_WRITE_LAST) ||
|
|
opcode == OP(RDMA_WRITE_LAST_WITH_IMMEDIATE))
|
|
break;
|
|
goto inv;
|
|
|
|
default:
|
|
if (opcode == OP(SEND_FIRST) ||
|
|
opcode == OP(SEND_ONLY) ||
|
|
opcode == OP(SEND_ONLY_WITH_IMMEDIATE) ||
|
|
opcode == OP(RDMA_WRITE_FIRST) ||
|
|
opcode == OP(RDMA_WRITE_ONLY) ||
|
|
opcode == OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE))
|
|
break;
|
|
goto inv;
|
|
}
|
|
|
|
/* OK, process the packet. */
|
|
switch (opcode) {
|
|
case OP(SEND_FIRST):
|
|
case OP(SEND_ONLY):
|
|
case OP(SEND_ONLY_WITH_IMMEDIATE):
|
|
send_first:
|
|
if (qp->r_reuse_sge) {
|
|
qp->r_reuse_sge = 0;
|
|
qp->r_sge = qp->s_rdma_read_sge;
|
|
} else if (!ipath_get_rwqe(qp, 0)) {
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
/* Save the WQE so we can reuse it in case of an error. */
|
|
qp->s_rdma_read_sge = qp->r_sge;
|
|
qp->r_rcv_len = 0;
|
|
if (opcode == OP(SEND_ONLY))
|
|
goto send_last;
|
|
else if (opcode == OP(SEND_ONLY_WITH_IMMEDIATE))
|
|
goto send_last_imm;
|
|
/* FALLTHROUGH */
|
|
case OP(SEND_MIDDLE):
|
|
/* Check for invalid length PMTU or posted rwqe len. */
|
|
if (unlikely(tlen != (hdrsize + pmtu + 4))) {
|
|
qp->r_reuse_sge = 1;
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
qp->r_rcv_len += pmtu;
|
|
if (unlikely(qp->r_rcv_len > qp->r_len)) {
|
|
qp->r_reuse_sge = 1;
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
ipath_copy_sge(&qp->r_sge, data, pmtu);
|
|
break;
|
|
|
|
case OP(SEND_LAST_WITH_IMMEDIATE):
|
|
send_last_imm:
|
|
if (header_in_data) {
|
|
wc.imm_data = *(__be32 *) data;
|
|
data += sizeof(__be32);
|
|
} else {
|
|
/* Immediate data comes after BTH */
|
|
wc.imm_data = ohdr->u.imm_data;
|
|
}
|
|
hdrsize += 4;
|
|
wc.wc_flags = IB_WC_WITH_IMM;
|
|
/* FALLTHROUGH */
|
|
case OP(SEND_LAST):
|
|
send_last:
|
|
/* Get the number of bytes the message was padded by. */
|
|
pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
|
|
/* Check for invalid length. */
|
|
/* XXX LAST len should be >= 1 */
|
|
if (unlikely(tlen < (hdrsize + pad + 4))) {
|
|
qp->r_reuse_sge = 1;
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
/* Don't count the CRC. */
|
|
tlen -= (hdrsize + pad + 4);
|
|
wc.byte_len = tlen + qp->r_rcv_len;
|
|
if (unlikely(wc.byte_len > qp->r_len)) {
|
|
qp->r_reuse_sge = 1;
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
/* XXX Need to free SGEs */
|
|
last_imm:
|
|
ipath_copy_sge(&qp->r_sge, data, tlen);
|
|
wc.wr_id = qp->r_wr_id;
|
|
wc.status = IB_WC_SUCCESS;
|
|
wc.opcode = IB_WC_RECV;
|
|
wc.vendor_err = 0;
|
|
wc.qp = &qp->ibqp;
|
|
wc.src_qp = qp->remote_qpn;
|
|
wc.pkey_index = 0;
|
|
wc.slid = qp->remote_ah_attr.dlid;
|
|
wc.sl = qp->remote_ah_attr.sl;
|
|
wc.dlid_path_bits = 0;
|
|
wc.port_num = 0;
|
|
/* Signal completion event if the solicited bit is set. */
|
|
ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc,
|
|
(ohdr->bth[0] &
|
|
__constant_cpu_to_be32(1 << 23)) != 0);
|
|
break;
|
|
|
|
case OP(RDMA_WRITE_FIRST):
|
|
case OP(RDMA_WRITE_ONLY):
|
|
case OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE): /* consume RWQE */
|
|
rdma_first:
|
|
/* RETH comes after BTH */
|
|
if (!header_in_data)
|
|
reth = &ohdr->u.rc.reth;
|
|
else {
|
|
reth = (struct ib_reth *)data;
|
|
data += sizeof(*reth);
|
|
}
|
|
hdrsize += sizeof(*reth);
|
|
qp->r_len = be32_to_cpu(reth->length);
|
|
qp->r_rcv_len = 0;
|
|
if (qp->r_len != 0) {
|
|
u32 rkey = be32_to_cpu(reth->rkey);
|
|
u64 vaddr = be64_to_cpu(reth->vaddr);
|
|
int ok;
|
|
|
|
/* Check rkey */
|
|
ok = ipath_rkey_ok(qp, &qp->r_sge, qp->r_len,
|
|
vaddr, rkey,
|
|
IB_ACCESS_REMOTE_WRITE);
|
|
if (unlikely(!ok)) {
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
} else {
|
|
qp->r_sge.sg_list = NULL;
|
|
qp->r_sge.sge.mr = NULL;
|
|
qp->r_sge.sge.vaddr = NULL;
|
|
qp->r_sge.sge.length = 0;
|
|
qp->r_sge.sge.sge_length = 0;
|
|
}
|
|
if (unlikely(!(qp->qp_access_flags &
|
|
IB_ACCESS_REMOTE_WRITE))) {
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
if (opcode == OP(RDMA_WRITE_ONLY))
|
|
goto rdma_last;
|
|
else if (opcode == OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE))
|
|
goto rdma_last_imm;
|
|
/* FALLTHROUGH */
|
|
case OP(RDMA_WRITE_MIDDLE):
|
|
/* Check for invalid length PMTU or posted rwqe len. */
|
|
if (unlikely(tlen != (hdrsize + pmtu + 4))) {
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
qp->r_rcv_len += pmtu;
|
|
if (unlikely(qp->r_rcv_len > qp->r_len)) {
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
ipath_copy_sge(&qp->r_sge, data, pmtu);
|
|
break;
|
|
|
|
case OP(RDMA_WRITE_LAST_WITH_IMMEDIATE):
|
|
rdma_last_imm:
|
|
if (header_in_data) {
|
|
wc.imm_data = *(__be32 *) data;
|
|
data += sizeof(__be32);
|
|
} else {
|
|
/* Immediate data comes after BTH */
|
|
wc.imm_data = ohdr->u.imm_data;
|
|
}
|
|
hdrsize += 4;
|
|
wc.wc_flags = IB_WC_WITH_IMM;
|
|
|
|
/* Get the number of bytes the message was padded by. */
|
|
pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
|
|
/* Check for invalid length. */
|
|
/* XXX LAST len should be >= 1 */
|
|
if (unlikely(tlen < (hdrsize + pad + 4))) {
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
/* Don't count the CRC. */
|
|
tlen -= (hdrsize + pad + 4);
|
|
if (unlikely(tlen + qp->r_rcv_len != qp->r_len)) {
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
if (qp->r_reuse_sge)
|
|
qp->r_reuse_sge = 0;
|
|
else if (!ipath_get_rwqe(qp, 1)) {
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
wc.byte_len = qp->r_len;
|
|
goto last_imm;
|
|
|
|
case OP(RDMA_WRITE_LAST):
|
|
rdma_last:
|
|
/* Get the number of bytes the message was padded by. */
|
|
pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3;
|
|
/* Check for invalid length. */
|
|
/* XXX LAST len should be >= 1 */
|
|
if (unlikely(tlen < (hdrsize + pad + 4))) {
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
/* Don't count the CRC. */
|
|
tlen -= (hdrsize + pad + 4);
|
|
if (unlikely(tlen + qp->r_rcv_len != qp->r_len)) {
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
ipath_copy_sge(&qp->r_sge, data, tlen);
|
|
break;
|
|
|
|
default:
|
|
/* Drop packet for unknown opcodes. */
|
|
dev->n_pkt_drops++;
|
|
goto done;
|
|
}
|
|
qp->r_psn++;
|
|
qp->r_state = opcode;
|
|
done:
|
|
return;
|
|
}
|