|
@@ -37,6 +37,19 @@
|
|
|
/* cut down ridiculously long IB macro names */
|
|
|
#define OP(x) IB_OPCODE_RC_##x
|
|
|
|
|
|
+static u32 restart_sge(struct ipath_sge_state *ss, struct ipath_swqe *wqe,
|
|
|
+ u32 psn, u32 pmtu)
|
|
|
+{
|
|
|
+ u32 len;
|
|
|
+
|
|
|
+ len = ((psn - wqe->psn) & IPATH_PSN_MASK) * pmtu;
|
|
|
+ ss->sge = wqe->sg_list[0];
|
|
|
+ ss->sg_list = wqe->sg_list + 1;
|
|
|
+ ss->num_sge = wqe->wr.num_sge;
|
|
|
+ ipath_skip_sge(ss, len);
|
|
|
+ return wqe->length - len;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* ipath_init_restart- initialize the qp->s_sge after a restart
|
|
|
* @qp: the QP who's SGE we're restarting
|
|
@@ -47,15 +60,9 @@
|
|
|
static void ipath_init_restart(struct ipath_qp *qp, struct ipath_swqe *wqe)
|
|
|
{
|
|
|
struct ipath_ibdev *dev;
|
|
|
- u32 len;
|
|
|
|
|
|
- len = ((qp->s_psn - wqe->psn) & IPATH_PSN_MASK) *
|
|
|
- ib_mtu_enum_to_int(qp->path_mtu);
|
|
|
- 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;
|
|
|
- ipath_skip_sge(&qp->s_sge, len);
|
|
|
- qp->s_len = wqe->length - len;
|
|
|
+ qp->s_len = restart_sge(&qp->s_sge, wqe, qp->s_psn,
|
|
|
+ ib_mtu_enum_to_int(qp->path_mtu));
|
|
|
dev = to_idev(qp->ibqp.device);
|
|
|
spin_lock(&dev->pending_lock);
|
|
|
if (list_empty(&qp->timerwait))
|
|
@@ -70,107 +77,123 @@ static void ipath_init_restart(struct ipath_qp *qp, struct ipath_swqe *wqe)
|
|
|
* @ohdr: a pointer to the IB header being constructed
|
|
|
* @pmtu: the path MTU
|
|
|
*
|
|
|
- * Return bth0 if constructed; otherwise, return 0.
|
|
|
+ * Return 1 if constructed; otherwise, return 0.
|
|
|
+ * Note that we are in the responder's side of the QP context.
|
|
|
* Note the QP s_lock must be held.
|
|
|
*/
|
|
|
-u32 ipath_make_rc_ack(struct ipath_qp *qp,
|
|
|
- struct ipath_other_headers *ohdr,
|
|
|
- u32 pmtu)
|
|
|
+static int ipath_make_rc_ack(struct ipath_qp *qp,
|
|
|
+ struct ipath_other_headers *ohdr,
|
|
|
+ u32 pmtu, u32 *bth0p, u32 *bth2p)
|
|
|
{
|
|
|
+ struct ipath_ack_entry *e;
|
|
|
u32 hwords;
|
|
|
u32 len;
|
|
|
u32 bth0;
|
|
|
+ u32 bth2;
|
|
|
|
|
|
/* header size in 32-bit words LRH+BTH = (8+12)/4. */
|
|
|
hwords = 5;
|
|
|
|
|
|
- /*
|
|
|
- * Send a response. Note that we are in the responder's
|
|
|
- * side of the QP context.
|
|
|
- */
|
|
|
switch (qp->s_ack_state) {
|
|
|
- case OP(RDMA_READ_REQUEST):
|
|
|
- qp->s_cur_sge = &qp->s_rdma_sge;
|
|
|
- len = qp->s_rdma_len;
|
|
|
- if (len > pmtu) {
|
|
|
- len = pmtu;
|
|
|
- qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST);
|
|
|
- } else
|
|
|
- qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY);
|
|
|
- qp->s_rdma_len -= len;
|
|
|
+ case OP(RDMA_READ_RESPONSE_LAST):
|
|
|
+ case OP(RDMA_READ_RESPONSE_ONLY):
|
|
|
+ case OP(ATOMIC_ACKNOWLEDGE):
|
|
|
+ qp->s_ack_state = OP(ACKNOWLEDGE);
|
|
|
+ /* FALLTHROUGH */
|
|
|
+ case OP(ACKNOWLEDGE):
|
|
|
+ /* Check for no next entry in the queue. */
|
|
|
+ if (qp->r_head_ack_queue == qp->s_tail_ack_queue) {
|
|
|
+ if (qp->s_flags & IPATH_S_ACK_PENDING)
|
|
|
+ goto normal;
|
|
|
+ goto bail;
|
|
|
+ }
|
|
|
+
|
|
|
+ e = &qp->s_ack_queue[qp->s_tail_ack_queue];
|
|
|
+ if (e->opcode == OP(RDMA_READ_REQUEST)) {
|
|
|
+ /* Copy SGE state in case we need to resend */
|
|
|
+ qp->s_ack_rdma_sge = e->rdma_sge;
|
|
|
+ qp->s_cur_sge = &qp->s_ack_rdma_sge;
|
|
|
+ len = e->rdma_sge.sge.sge_length;
|
|
|
+ if (len > pmtu) {
|
|
|
+ len = pmtu;
|
|
|
+ qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST);
|
|
|
+ } else {
|
|
|
+ qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY);
|
|
|
+ if (++qp->s_tail_ack_queue >
|
|
|
+ IPATH_MAX_RDMA_ATOMIC)
|
|
|
+ qp->s_tail_ack_queue = 0;
|
|
|
+ }
|
|
|
+ ohdr->u.aeth = ipath_compute_aeth(qp);
|
|
|
+ hwords++;
|
|
|
+ qp->s_ack_rdma_psn = e->psn;
|
|
|
+ bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK;
|
|
|
+ } else {
|
|
|
+ /* COMPARE_SWAP or FETCH_ADD */
|
|
|
+ qp->s_cur_sge = NULL;
|
|
|
+ len = 0;
|
|
|
+ qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE);
|
|
|
+ ohdr->u.at.aeth = ipath_compute_aeth(qp);
|
|
|
+ ohdr->u.at.atomic_ack_eth[0] =
|
|
|
+ cpu_to_be32(e->atomic_data >> 32);
|
|
|
+ ohdr->u.at.atomic_ack_eth[1] =
|
|
|
+ cpu_to_be32(e->atomic_data);
|
|
|
+ hwords += sizeof(ohdr->u.at) / sizeof(u32);
|
|
|
+ bth2 = e->psn;
|
|
|
+ if (++qp->s_tail_ack_queue > IPATH_MAX_RDMA_ATOMIC)
|
|
|
+ qp->s_tail_ack_queue = 0;
|
|
|
+ }
|
|
|
bth0 = qp->s_ack_state << 24;
|
|
|
- ohdr->u.aeth = ipath_compute_aeth(qp);
|
|
|
- hwords++;
|
|
|
break;
|
|
|
|
|
|
case OP(RDMA_READ_RESPONSE_FIRST):
|
|
|
qp->s_ack_state = OP(RDMA_READ_RESPONSE_MIDDLE);
|
|
|
/* FALLTHROUGH */
|
|
|
case OP(RDMA_READ_RESPONSE_MIDDLE):
|
|
|
- qp->s_cur_sge = &qp->s_rdma_sge;
|
|
|
- len = qp->s_rdma_len;
|
|
|
+ len = qp->s_ack_rdma_sge.sge.sge_length;
|
|
|
if (len > pmtu)
|
|
|
len = pmtu;
|
|
|
else {
|
|
|
ohdr->u.aeth = ipath_compute_aeth(qp);
|
|
|
hwords++;
|
|
|
qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST);
|
|
|
+ if (++qp->s_tail_ack_queue > IPATH_MAX_RDMA_ATOMIC)
|
|
|
+ qp->s_tail_ack_queue = 0;
|
|
|
}
|
|
|
- qp->s_rdma_len -= len;
|
|
|
bth0 = qp->s_ack_state << 24;
|
|
|
- break;
|
|
|
-
|
|
|
- case OP(RDMA_READ_RESPONSE_LAST):
|
|
|
- case OP(RDMA_READ_RESPONSE_ONLY):
|
|
|
- /*
|
|
|
- * We have to prevent new requests from changing
|
|
|
- * the r_sge state while a ipath_verbs_send()
|
|
|
- * is in progress.
|
|
|
- */
|
|
|
- qp->s_ack_state = OP(ACKNOWLEDGE);
|
|
|
- bth0 = 0;
|
|
|
- goto bail;
|
|
|
-
|
|
|
- case OP(COMPARE_SWAP):
|
|
|
- case OP(FETCH_ADD):
|
|
|
- qp->s_cur_sge = NULL;
|
|
|
- len = 0;
|
|
|
- /*
|
|
|
- * Set the s_ack_state so the receive interrupt handler
|
|
|
- * won't try to send an ACK (out of order) until this one
|
|
|
- * is actually sent.
|
|
|
- */
|
|
|
- qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST);
|
|
|
- bth0 = OP(ATOMIC_ACKNOWLEDGE) << 24;
|
|
|
- ohdr->u.at.aeth = ipath_compute_aeth(qp);
|
|
|
- ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->r_atomic_data);
|
|
|
- hwords += sizeof(ohdr->u.at) / 4;
|
|
|
+ bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK;
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
- /* Send a regular ACK. */
|
|
|
- qp->s_cur_sge = NULL;
|
|
|
- len = 0;
|
|
|
+ normal:
|
|
|
/*
|
|
|
- * Set the s_ack_state so the receive interrupt handler
|
|
|
- * won't try to send an ACK (out of order) until this one
|
|
|
- * is actually sent.
|
|
|
+ * Send a regular ACK.
|
|
|
+ * Set the s_ack_state so we wait until after sending
|
|
|
+ * the ACK before setting s_ack_state to ACKNOWLEDGE
|
|
|
+ * (see above).
|
|
|
*/
|
|
|
- qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST);
|
|
|
- bth0 = OP(ACKNOWLEDGE) << 24;
|
|
|
+ qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE);
|
|
|
+ qp->s_flags &= ~IPATH_S_ACK_PENDING;
|
|
|
+ qp->s_cur_sge = NULL;
|
|
|
if (qp->s_nak_state)
|
|
|
- ohdr->u.aeth = cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) |
|
|
|
- (qp->s_nak_state <<
|
|
|
- IPATH_AETH_CREDIT_SHIFT));
|
|
|
+ ohdr->u.aeth =
|
|
|
+ cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) |
|
|
|
+ (qp->s_nak_state <<
|
|
|
+ IPATH_AETH_CREDIT_SHIFT));
|
|
|
else
|
|
|
ohdr->u.aeth = ipath_compute_aeth(qp);
|
|
|
hwords++;
|
|
|
+ len = 0;
|
|
|
+ bth0 = OP(ACKNOWLEDGE) << 24;
|
|
|
+ bth2 = qp->s_ack_psn & IPATH_PSN_MASK;
|
|
|
}
|
|
|
qp->s_hdrwords = hwords;
|
|
|
qp->s_cur_size = len;
|
|
|
+ *bth0p = bth0;
|
|
|
+ *bth2p = bth2;
|
|
|
+ return 1;
|
|
|
|
|
|
bail:
|
|
|
- return bth0;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -197,9 +220,16 @@ int ipath_make_rc_req(struct ipath_qp *qp,
|
|
|
u32 bth2;
|
|
|
char newreq;
|
|
|
|
|
|
+ /* Sending responses has higher priority over sending requests. */
|
|
|
+ if ((qp->r_head_ack_queue != qp->s_tail_ack_queue ||
|
|
|
+ (qp->s_flags & IPATH_S_ACK_PENDING) ||
|
|
|
+ qp->s_ack_state != IB_OPCODE_RC_ACKNOWLEDGE) &&
|
|
|
+ ipath_make_rc_ack(qp, ohdr, pmtu, bth0p, bth2p))
|
|
|
+ goto done;
|
|
|
+
|
|
|
if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK) ||
|
|
|
qp->s_rnr_timeout)
|
|
|
- goto done;
|
|
|
+ goto bail;
|
|
|
|
|
|
/* Limit the number of packets sent without an ACK. */
|
|
|
if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT) > 0) {
|
|
@@ -210,7 +240,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
|
|
|
list_add_tail(&qp->timerwait,
|
|
|
&dev->pending[dev->pending_index]);
|
|
|
spin_unlock(&dev->pending_lock);
|
|
|
- goto done;
|
|
|
+ goto bail;
|
|
|
}
|
|
|
|
|
|
/* header size in 32-bit words LRH+BTH = (8+12)/4. */
|
|
@@ -232,7 +262,16 @@ int ipath_make_rc_req(struct ipath_qp *qp,
|
|
|
if (qp->s_cur == qp->s_tail) {
|
|
|
/* Check if send work queue is empty. */
|
|
|
if (qp->s_tail == qp->s_head)
|
|
|
- goto done;
|
|
|
+ goto bail;
|
|
|
+ /*
|
|
|
+ * If a fence is requested, wait for previous
|
|
|
+ * RDMA read and atomic operations to finish.
|
|
|
+ */
|
|
|
+ if ((wqe->wr.send_flags & IB_SEND_FENCE) &&
|
|
|
+ qp->s_num_rd_atomic) {
|
|
|
+ qp->s_flags |= IPATH_S_FENCE_PENDING;
|
|
|
+ goto bail;
|
|
|
+ }
|
|
|
wqe->psn = qp->s_next_psn;
|
|
|
newreq = 1;
|
|
|
}
|
|
@@ -250,7 +289,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
|
|
|
/* If no credit, return. */
|
|
|
if (qp->s_lsn != (u32) -1 &&
|
|
|
ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0)
|
|
|
- goto done;
|
|
|
+ goto bail;
|
|
|
wqe->lpsn = wqe->psn;
|
|
|
if (len > pmtu) {
|
|
|
wqe->lpsn += (len - 1) / pmtu;
|
|
@@ -281,13 +320,13 @@ int ipath_make_rc_req(struct ipath_qp *qp,
|
|
|
/* If no credit, return. */
|
|
|
if (qp->s_lsn != (u32) -1 &&
|
|
|
ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0)
|
|
|
- goto done;
|
|
|
+ goto bail;
|
|
|
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;
|
|
|
+ hwords += sizeof(struct ib_reth) / sizeof(u32);
|
|
|
wqe->lpsn = wqe->psn;
|
|
|
if (len > pmtu) {
|
|
|
wqe->lpsn += (len - 1) / pmtu;
|
|
@@ -312,14 +351,17 @@ int ipath_make_rc_req(struct ipath_qp *qp,
|
|
|
break;
|
|
|
|
|
|
case IB_WR_RDMA_READ:
|
|
|
- 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);
|
|
|
- qp->s_state = OP(RDMA_READ_REQUEST);
|
|
|
- hwords += sizeof(ohdr->u.rc.reth) / 4;
|
|
|
+ /*
|
|
|
+ * Don't allow more operations to be started
|
|
|
+ * than the QP limits allow.
|
|
|
+ */
|
|
|
if (newreq) {
|
|
|
+ if (qp->s_num_rd_atomic >=
|
|
|
+ qp->s_max_rd_atomic) {
|
|
|
+ qp->s_flags |= IPATH_S_RDMAR_PENDING;
|
|
|
+ goto bail;
|
|
|
+ }
|
|
|
+ qp->s_num_rd_atomic++;
|
|
|
if (qp->s_lsn != (u32) -1)
|
|
|
qp->s_lsn++;
|
|
|
/*
|
|
@@ -330,6 +372,13 @@ int ipath_make_rc_req(struct ipath_qp *qp,
|
|
|
qp->s_next_psn += (len - 1) / pmtu;
|
|
|
wqe->lpsn = qp->s_next_psn++;
|
|
|
}
|
|
|
+ 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);
|
|
|
+ qp->s_state = OP(RDMA_READ_REQUEST);
|
|
|
+ hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32);
|
|
|
ss = NULL;
|
|
|
len = 0;
|
|
|
if (++qp->s_cur == qp->s_size)
|
|
@@ -338,32 +387,48 @@ int ipath_make_rc_req(struct ipath_qp *qp,
|
|
|
|
|
|
case IB_WR_ATOMIC_CMP_AND_SWP:
|
|
|
case IB_WR_ATOMIC_FETCH_AND_ADD:
|
|
|
- if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP)
|
|
|
- qp->s_state = OP(COMPARE_SWAP);
|
|
|
- else
|
|
|
- qp->s_state = OP(FETCH_ADD);
|
|
|
- ohdr->u.atomic_eth.vaddr = cpu_to_be64(
|
|
|
- wqe->wr.wr.atomic.remote_addr);
|
|
|
- ohdr->u.atomic_eth.rkey = cpu_to_be32(
|
|
|
- wqe->wr.wr.atomic.rkey);
|
|
|
- ohdr->u.atomic_eth.swap_data = cpu_to_be64(
|
|
|
- wqe->wr.wr.atomic.swap);
|
|
|
- ohdr->u.atomic_eth.compare_data = cpu_to_be64(
|
|
|
- wqe->wr.wr.atomic.compare_add);
|
|
|
- hwords += sizeof(struct ib_atomic_eth) / 4;
|
|
|
+ /*
|
|
|
+ * Don't allow more operations to be started
|
|
|
+ * than the QP limits allow.
|
|
|
+ */
|
|
|
if (newreq) {
|
|
|
+ if (qp->s_num_rd_atomic >=
|
|
|
+ qp->s_max_rd_atomic) {
|
|
|
+ qp->s_flags |= IPATH_S_RDMAR_PENDING;
|
|
|
+ goto bail;
|
|
|
+ }
|
|
|
+ qp->s_num_rd_atomic++;
|
|
|
if (qp->s_lsn != (u32) -1)
|
|
|
qp->s_lsn++;
|
|
|
wqe->lpsn = wqe->psn;
|
|
|
}
|
|
|
- if (++qp->s_cur == qp->s_size)
|
|
|
- qp->s_cur = 0;
|
|
|
+ if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP) {
|
|
|
+ qp->s_state = OP(COMPARE_SWAP);
|
|
|
+ ohdr->u.atomic_eth.swap_data = cpu_to_be64(
|
|
|
+ wqe->wr.wr.atomic.swap);
|
|
|
+ ohdr->u.atomic_eth.compare_data = cpu_to_be64(
|
|
|
+ wqe->wr.wr.atomic.compare_add);
|
|
|
+ } else {
|
|
|
+ qp->s_state = OP(FETCH_ADD);
|
|
|
+ ohdr->u.atomic_eth.swap_data = cpu_to_be64(
|
|
|
+ wqe->wr.wr.atomic.compare_add);
|
|
|
+ ohdr->u.atomic_eth.compare_data = 0;
|
|
|
+ }
|
|
|
+ ohdr->u.atomic_eth.vaddr[0] = cpu_to_be32(
|
|
|
+ wqe->wr.wr.atomic.remote_addr >> 32);
|
|
|
+ ohdr->u.atomic_eth.vaddr[1] = cpu_to_be32(
|
|
|
+ wqe->wr.wr.atomic.remote_addr);
|
|
|
+ ohdr->u.atomic_eth.rkey = cpu_to_be32(
|
|
|
+ wqe->wr.wr.atomic.rkey);
|
|
|
+ hwords += sizeof(struct ib_atomic_eth) / sizeof(u32);
|
|
|
ss = NULL;
|
|
|
len = 0;
|
|
|
+ if (++qp->s_cur == qp->s_size)
|
|
|
+ qp->s_cur = 0;
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
- goto done;
|
|
|
+ goto bail;
|
|
|
}
|
|
|
qp->s_sge.sge = wqe->sg_list[0];
|
|
|
qp->s_sge.sg_list = wqe->sg_list + 1;
|
|
@@ -479,7 +544,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
|
|
|
cpu_to_be32(wqe->wr.wr.rdma.rkey);
|
|
|
ohdr->u.rc.reth.length = cpu_to_be32(qp->s_len);
|
|
|
qp->s_state = OP(RDMA_READ_REQUEST);
|
|
|
- hwords += sizeof(ohdr->u.rc.reth) / 4;
|
|
|
+ hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32);
|
|
|
bth2 = qp->s_psn++ & IPATH_PSN_MASK;
|
|
|
if ((int)(qp->s_psn - qp->s_next_psn) > 0)
|
|
|
qp->s_next_psn = qp->s_psn;
|
|
@@ -489,20 +554,6 @@ int ipath_make_rc_req(struct ipath_qp *qp,
|
|
|
if (qp->s_cur == qp->s_size)
|
|
|
qp->s_cur = 0;
|
|
|
break;
|
|
|
-
|
|
|
- case OP(RDMA_READ_REQUEST):
|
|
|
- case OP(COMPARE_SWAP):
|
|
|
- case OP(FETCH_ADD):
|
|
|
- /*
|
|
|
- * We shouldn't start anything new until this request is
|
|
|
- * finished. The ACK will handle rescheduling us. XXX The
|
|
|
- * number of outstanding ones is negotiated at connection
|
|
|
- * setup time (see pg. 258,289)? XXX Also, if we support
|
|
|
- * multiple outstanding requests, we need to check the WQE
|
|
|
- * IB_SEND_FENCE flag and not send a new request if a RDMA
|
|
|
- * read or atomic is pending.
|
|
|
- */
|
|
|
- goto done;
|
|
|
}
|
|
|
if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT - 1) >= 0)
|
|
|
bth2 |= 1 << 31; /* Request ACK. */
|
|
@@ -512,9 +563,10 @@ int ipath_make_rc_req(struct ipath_qp *qp,
|
|
|
qp->s_cur_size = len;
|
|
|
*bth0p = bth0 | (qp->s_state << 24);
|
|
|
*bth2p = bth2;
|
|
|
+done:
|
|
|
return 1;
|
|
|
|
|
|
-done:
|
|
|
+bail:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -524,7 +576,8 @@ done:
|
|
|
*
|
|
|
* This is called from ipath_rc_rcv() and only uses the receive
|
|
|
* side QP state.
|
|
|
- * Note that RDMA reads are handled in the send side QP state and tasklet.
|
|
|
+ * Note that RDMA reads and atomics are handled in the
|
|
|
+ * send side QP state and tasklet.
|
|
|
*/
|
|
|
static void send_rc_ack(struct ipath_qp *qp)
|
|
|
{
|
|
@@ -535,6 +588,10 @@ static void send_rc_ack(struct ipath_qp *qp)
|
|
|
struct ipath_ib_header hdr;
|
|
|
struct ipath_other_headers *ohdr;
|
|
|
|
|
|
+ /* Don't send ACK or NAK if a RDMA read or atomic is pending. */
|
|
|
+ if (qp->r_head_ack_queue != qp->s_tail_ack_queue)
|
|
|
+ goto queue_ack;
|
|
|
+
|
|
|
/* Construct the header. */
|
|
|
ohdr = &hdr.u.oth;
|
|
|
lrh0 = IPATH_LRH_BTH;
|
|
@@ -548,19 +605,14 @@ static void send_rc_ack(struct ipath_qp *qp)
|
|
|
lrh0 = IPATH_LRH_GRH;
|
|
|
}
|
|
|
/* read pkey_index w/o lock (its atomic) */
|
|
|
- bth0 = ipath_get_pkey(dev->dd, qp->s_pkey_index);
|
|
|
+ bth0 = ipath_get_pkey(dev->dd, qp->s_pkey_index) |
|
|
|
+ OP(ACKNOWLEDGE) << 24;
|
|
|
if (qp->r_nak_state)
|
|
|
ohdr->u.aeth = cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) |
|
|
|
(qp->r_nak_state <<
|
|
|
IPATH_AETH_CREDIT_SHIFT));
|
|
|
else
|
|
|
ohdr->u.aeth = ipath_compute_aeth(qp);
|
|
|
- if (qp->r_ack_state >= OP(COMPARE_SWAP)) {
|
|
|
- bth0 |= OP(ATOMIC_ACKNOWLEDGE) << 24;
|
|
|
- ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->r_atomic_data);
|
|
|
- hwords += sizeof(ohdr->u.at.atomic_ack_eth) / 4;
|
|
|
- } else
|
|
|
- bth0 |= OP(ACKNOWLEDGE) << 24;
|
|
|
lrh0 |= qp->remote_ah_attr.sl << 4;
|
|
|
hdr.lrh[0] = cpu_to_be16(lrh0);
|
|
|
hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid);
|
|
@@ -574,31 +626,31 @@ static void send_rc_ack(struct ipath_qp *qp)
|
|
|
* If we can send the ACK, clear the ACK state.
|
|
|
*/
|
|
|
if (ipath_verbs_send(dev->dd, hwords, (u32 *) &hdr, 0, NULL) == 0) {
|
|
|
- qp->r_ack_state = OP(ACKNOWLEDGE);
|
|
|
dev->n_unicast_xmit++;
|
|
|
- } else {
|
|
|
- /*
|
|
|
- * We are out of PIO buffers at the moment.
|
|
|
- * Pass responsibility for sending the ACK to the
|
|
|
- * send tasklet so that when a PIO buffer becomes
|
|
|
- * available, the ACK is sent ahead of other outgoing
|
|
|
- * packets.
|
|
|
- */
|
|
|
- dev->n_rc_qacks++;
|
|
|
- spin_lock_irq(&qp->s_lock);
|
|
|
- /* Don't coalesce if a RDMA read or atomic is pending. */
|
|
|
- if (qp->s_ack_state == OP(ACKNOWLEDGE) ||
|
|
|
- qp->s_ack_state < OP(RDMA_READ_REQUEST)) {
|
|
|
- qp->s_ack_state = qp->r_ack_state;
|
|
|
- qp->s_nak_state = qp->r_nak_state;
|
|
|
- qp->s_ack_psn = qp->r_ack_psn;
|
|
|
- qp->r_ack_state = OP(ACKNOWLEDGE);
|
|
|
- }
|
|
|
- spin_unlock_irq(&qp->s_lock);
|
|
|
-
|
|
|
- /* Call ipath_do_rc_send() in another thread. */
|
|
|
- tasklet_hi_schedule(&qp->s_task);
|
|
|
+ goto done;
|
|
|
}
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We are out of PIO buffers at the moment.
|
|
|
+ * Pass responsibility for sending the ACK to the
|
|
|
+ * send tasklet so that when a PIO buffer becomes
|
|
|
+ * available, the ACK is sent ahead of other outgoing
|
|
|
+ * packets.
|
|
|
+ */
|
|
|
+ dev->n_rc_qacks++;
|
|
|
+
|
|
|
+queue_ack:
|
|
|
+ spin_lock_irq(&qp->s_lock);
|
|
|
+ qp->s_flags |= IPATH_S_ACK_PENDING;
|
|
|
+ qp->s_nak_state = qp->r_nak_state;
|
|
|
+ qp->s_ack_psn = qp->r_ack_psn;
|
|
|
+ spin_unlock_irq(&qp->s_lock);
|
|
|
+
|
|
|
+ /* Call ipath_do_rc_send() in another thread. */
|
|
|
+ tasklet_hi_schedule(&qp->s_task);
|
|
|
+
|
|
|
+done:
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -775,10 +827,6 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
|
|
|
list_del_init(&qp->timerwait);
|
|
|
spin_unlock(&dev->pending_lock);
|
|
|
|
|
|
- /* Nothing is pending to ACK/NAK. */
|
|
|
- if (unlikely(qp->s_last == qp->s_tail))
|
|
|
- goto bail;
|
|
|
-
|
|
|
/*
|
|
|
* Note that NAKs implicitly ACK outstanding SEND and RDMA write
|
|
|
* requests and implicitly NAK RDMA read and atomic requests issued
|
|
@@ -806,7 +854,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
|
|
|
*/
|
|
|
if ((wqe->wr.opcode == IB_WR_RDMA_READ &&
|
|
|
(opcode != OP(RDMA_READ_RESPONSE_LAST) ||
|
|
|
- ipath_cmp24(ack_psn, wqe->lpsn) != 0)) ||
|
|
|
+ ipath_cmp24(ack_psn, wqe->lpsn) != 0)) ||
|
|
|
((wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
|
|
|
wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) &&
|
|
|
(opcode != OP(ATOMIC_ACKNOWLEDGE) ||
|
|
@@ -824,12 +872,23 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
|
|
|
*/
|
|
|
goto bail;
|
|
|
}
|
|
|
- if (wqe->wr.opcode == IB_WR_RDMA_READ ||
|
|
|
- wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
|
|
|
- wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD)
|
|
|
- tasklet_hi_schedule(&qp->s_task);
|
|
|
+ if (qp->s_num_rd_atomic &&
|
|
|
+ (wqe->wr.opcode == IB_WR_RDMA_READ ||
|
|
|
+ wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
|
|
|
+ wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD)) {
|
|
|
+ qp->s_num_rd_atomic--;
|
|
|
+ /* Restart sending task if fence is complete */
|
|
|
+ if ((qp->s_flags & IPATH_S_FENCE_PENDING) &&
|
|
|
+ !qp->s_num_rd_atomic) {
|
|
|
+ qp->s_flags &= ~IPATH_S_FENCE_PENDING;
|
|
|
+ tasklet_hi_schedule(&qp->s_task);
|
|
|
+ } else if (qp->s_flags & IPATH_S_RDMAR_PENDING) {
|
|
|
+ qp->s_flags &= ~IPATH_S_RDMAR_PENDING;
|
|
|
+ tasklet_hi_schedule(&qp->s_task);
|
|
|
+ }
|
|
|
+ }
|
|
|
/* Post a send completion queue entry if requested. */
|
|
|
- if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) ||
|
|
|
+ if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) ||
|
|
|
(wqe->wr.send_flags & IB_SEND_SIGNALED)) {
|
|
|
wc.wr_id = wqe->wr.wr_id;
|
|
|
wc.status = IB_WC_SUCCESS;
|
|
@@ -1003,6 +1062,7 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
|
|
|
u32 psn, u32 hdrsize, u32 pmtu,
|
|
|
int header_in_data)
|
|
|
{
|
|
|
+ struct ipath_swqe *wqe;
|
|
|
unsigned long flags;
|
|
|
struct ib_wc wc;
|
|
|
int diff;
|
|
@@ -1032,6 +1092,10 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
|
|
|
goto ack_done;
|
|
|
}
|
|
|
|
|
|
+ if (unlikely(qp->s_last == qp->s_tail))
|
|
|
+ goto ack_done;
|
|
|
+ wqe = get_swqe_ptr(qp, qp->s_last);
|
|
|
+
|
|
|
switch (opcode) {
|
|
|
case OP(ACKNOWLEDGE):
|
|
|
case OP(ATOMIC_ACKNOWLEDGE):
|
|
@@ -1042,38 +1106,49 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
|
|
|
aeth = be32_to_cpu(((__be32 *) data)[0]);
|
|
|
data += sizeof(__be32);
|
|
|
}
|
|
|
- if (opcode == OP(ATOMIC_ACKNOWLEDGE))
|
|
|
- *(u64 *) qp->s_sge.sge.vaddr = *(u64 *) data;
|
|
|
+ if (opcode == OP(ATOMIC_ACKNOWLEDGE)) {
|
|
|
+ u64 val;
|
|
|
+
|
|
|
+ if (!header_in_data) {
|
|
|
+ __be32 *p = ohdr->u.at.atomic_ack_eth;
|
|
|
+
|
|
|
+ val = ((u64) be32_to_cpu(p[0]) << 32) |
|
|
|
+ be32_to_cpu(p[1]);
|
|
|
+ } else
|
|
|
+ val = be64_to_cpu(((__be64 *) data)[0]);
|
|
|
+ *(u64 *) wqe->sg_list[0].vaddr = val;
|
|
|
+ }
|
|
|
if (!do_rc_ack(qp, aeth, psn, opcode) ||
|
|
|
opcode != OP(RDMA_READ_RESPONSE_FIRST))
|
|
|
goto ack_done;
|
|
|
hdrsize += 4;
|
|
|
+ if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ))
|
|
|
+ goto ack_done;
|
|
|
/*
|
|
|
- * do_rc_ack() has already checked the PSN so skip
|
|
|
- * the sequence check.
|
|
|
+ * If this is a response to a resent RDMA read, we
|
|
|
+ * have to be careful to copy the data to the right
|
|
|
+ * location.
|
|
|
*/
|
|
|
- goto rdma_read;
|
|
|
+ qp->s_rdma_read_len = restart_sge(&qp->s_rdma_read_sge,
|
|
|
+ wqe, psn, pmtu);
|
|
|
+ goto read_middle;
|
|
|
|
|
|
case OP(RDMA_READ_RESPONSE_MIDDLE):
|
|
|
/* no AETH, no ACK */
|
|
|
if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) {
|
|
|
dev->n_rdma_seq++;
|
|
|
- if (qp->s_last != qp->s_tail)
|
|
|
- ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
|
|
|
+ ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
|
|
|
goto ack_done;
|
|
|
}
|
|
|
- rdma_read:
|
|
|
- if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST)))
|
|
|
+ if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ))
|
|
|
goto ack_done;
|
|
|
+ read_middle:
|
|
|
if (unlikely(tlen != (hdrsize + pmtu + 4)))
|
|
|
goto ack_done;
|
|
|
- if (unlikely(pmtu >= qp->s_len))
|
|
|
+ if (unlikely(pmtu >= qp->s_rdma_read_len))
|
|
|
goto ack_done;
|
|
|
+
|
|
|
/* We got a response so update the timeout. */
|
|
|
- if (unlikely(qp->s_last == qp->s_tail ||
|
|
|
- get_swqe_ptr(qp, qp->s_last)->wr.opcode !=
|
|
|
- IB_WR_RDMA_READ))
|
|
|
- goto ack_done;
|
|
|
spin_lock(&dev->pending_lock);
|
|
|
if (qp->s_rnr_timeout == 0 && !list_empty(&qp->timerwait))
|
|
|
list_move_tail(&qp->timerwait,
|
|
@@ -1082,27 +1157,41 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
|
|
|
/*
|
|
|
* Update the RDMA receive state but do the copy w/o
|
|
|
* holding the locks and blocking interrupts.
|
|
|
- * XXX Yet another place that affects relaxed RDMA order
|
|
|
- * since we don't want s_sge modified.
|
|
|
*/
|
|
|
- qp->s_len -= pmtu;
|
|
|
+ qp->s_rdma_read_len -= pmtu;
|
|
|
update_last_psn(qp, psn);
|
|
|
spin_unlock_irqrestore(&qp->s_lock, flags);
|
|
|
- ipath_copy_sge(&qp->s_sge, data, pmtu);
|
|
|
+ ipath_copy_sge(&qp->s_rdma_read_sge, data, pmtu);
|
|
|
goto bail;
|
|
|
|
|
|
+ case OP(RDMA_READ_RESPONSE_ONLY):
|
|
|
+ if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) {
|
|
|
+ dev->n_rdma_seq++;
|
|
|
+ ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
|
|
|
+ goto ack_done;
|
|
|
+ }
|
|
|
+ if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ))
|
|
|
+ goto ack_done;
|
|
|
+ /*
|
|
|
+ * If this is a response to a resent RDMA read, we
|
|
|
+ * have to be careful to copy the data to the right
|
|
|
+ * location.
|
|
|
+ * XXX should check PSN and wqe opcode first.
|
|
|
+ */
|
|
|
+ qp->s_rdma_read_len = restart_sge(&qp->s_rdma_read_sge,
|
|
|
+ wqe, psn, pmtu);
|
|
|
+ goto read_last;
|
|
|
+
|
|
|
case OP(RDMA_READ_RESPONSE_LAST):
|
|
|
/* ACKs READ req. */
|
|
|
if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) {
|
|
|
dev->n_rdma_seq++;
|
|
|
- if (qp->s_last != qp->s_tail)
|
|
|
- ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
|
|
|
+ ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
|
|
|
goto ack_done;
|
|
|
}
|
|
|
- /* FALLTHROUGH */
|
|
|
- case OP(RDMA_READ_RESPONSE_ONLY):
|
|
|
- if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST)))
|
|
|
+ if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ))
|
|
|
goto ack_done;
|
|
|
+ read_last:
|
|
|
/*
|
|
|
* Get the number of bytes the message was padded by.
|
|
|
*/
|
|
@@ -1117,7 +1206,7 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
|
|
|
goto ack_done;
|
|
|
}
|
|
|
tlen -= hdrsize + pad + 8;
|
|
|
- if (unlikely(tlen != qp->s_len)) {
|
|
|
+ if (unlikely(tlen != qp->s_rdma_read_len)) {
|
|
|
/* XXX Need to generate an error CQ entry. */
|
|
|
goto ack_done;
|
|
|
}
|
|
@@ -1127,17 +1216,8 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
|
|
|
aeth = be32_to_cpu(((__be32 *) data)[0]);
|
|
|
data += sizeof(__be32);
|
|
|
}
|
|
|
- ipath_copy_sge(&qp->s_sge, data, tlen);
|
|
|
- if (do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST))) {
|
|
|
- /*
|
|
|
- * Change the state so we contimue
|
|
|
- * processing new requests and wake up the
|
|
|
- * tasklet if there are posted sends.
|
|
|
- */
|
|
|
- qp->s_state = OP(SEND_LAST);
|
|
|
- if (qp->s_tail != qp->s_head)
|
|
|
- tasklet_hi_schedule(&qp->s_task);
|
|
|
- }
|
|
|
+ ipath_copy_sge(&qp->s_rdma_read_sge, data, tlen);
|
|
|
+ (void) do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST));
|
|
|
goto ack_done;
|
|
|
}
|
|
|
|
|
@@ -1162,7 +1242,7 @@ bail:
|
|
|
* incoming RC packet for the given QP.
|
|
|
* Called at interrupt level.
|
|
|
* Return 1 if no more processing is needed; otherwise return 0 to
|
|
|
- * schedule a response to be sent and the s_lock unlocked.
|
|
|
+ * schedule a response to be sent.
|
|
|
*/
|
|
|
static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev,
|
|
|
struct ipath_other_headers *ohdr,
|
|
@@ -1173,25 +1253,23 @@ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev,
|
|
|
int diff,
|
|
|
int header_in_data)
|
|
|
{
|
|
|
- struct ib_reth *reth;
|
|
|
+ struct ipath_ack_entry *e;
|
|
|
+ u8 i, prev;
|
|
|
+ int old_req;
|
|
|
|
|
|
if (diff > 0) {
|
|
|
/*
|
|
|
* Packet sequence error.
|
|
|
* A NAK will ACK earlier sends and RDMA writes.
|
|
|
- * Don't queue the NAK if a RDMA read, atomic, or
|
|
|
- * NAK is pending though.
|
|
|
+ * Don't queue the NAK if we already sent one.
|
|
|
*/
|
|
|
- if (qp->s_ack_state != OP(ACKNOWLEDGE) ||
|
|
|
- qp->r_nak_state != 0)
|
|
|
- goto done;
|
|
|
- if (qp->r_ack_state < OP(COMPARE_SWAP)) {
|
|
|
- qp->r_ack_state = OP(SEND_ONLY);
|
|
|
+ if (!qp->r_nak_state) {
|
|
|
qp->r_nak_state = IB_NAK_PSN_ERROR;
|
|
|
/* Use the expected PSN. */
|
|
|
qp->r_ack_psn = qp->r_psn;
|
|
|
+ goto send_ack;
|
|
|
}
|
|
|
- goto send_ack;
|
|
|
+ goto done;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1204,8 +1282,46 @@ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev,
|
|
|
* can coalesce an outstanding duplicate ACK. We have to
|
|
|
* send the earliest so that RDMA reads can be restarted at
|
|
|
* the requester's expected PSN.
|
|
|
+ *
|
|
|
+ * First, find where this duplicate PSN falls within the
|
|
|
+ * ACKs previously sent.
|
|
|
*/
|
|
|
- if (opcode == OP(RDMA_READ_REQUEST)) {
|
|
|
+ psn &= IPATH_PSN_MASK;
|
|
|
+ e = NULL;
|
|
|
+ old_req = 1;
|
|
|
+ spin_lock_irq(&qp->s_lock);
|
|
|
+ for (i = qp->r_head_ack_queue; ; i = prev) {
|
|
|
+ if (i == qp->s_tail_ack_queue)
|
|
|
+ old_req = 0;
|
|
|
+ if (i)
|
|
|
+ prev = i - 1;
|
|
|
+ else
|
|
|
+ prev = IPATH_MAX_RDMA_ATOMIC;
|
|
|
+ if (prev == qp->r_head_ack_queue) {
|
|
|
+ e = NULL;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ e = &qp->s_ack_queue[prev];
|
|
|
+ if (!e->opcode) {
|
|
|
+ e = NULL;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (ipath_cmp24(psn, e->psn) >= 0)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ switch (opcode) {
|
|
|
+ case OP(RDMA_READ_REQUEST): {
|
|
|
+ struct ib_reth *reth;
|
|
|
+ u32 offset;
|
|
|
+ u32 len;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we didn't find the RDMA read request in the ack queue,
|
|
|
+ * or the send tasklet is already backed up to send an
|
|
|
+ * earlier entry, we can ignore this request.
|
|
|
+ */
|
|
|
+ if (!e || e->opcode != OP(RDMA_READ_REQUEST) || old_req)
|
|
|
+ goto unlock_done;
|
|
|
/* RETH comes after BTH */
|
|
|
if (!header_in_data)
|
|
|
reth = &ohdr->u.rc.reth;
|
|
@@ -1214,88 +1330,87 @@ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev,
|
|
|
data += sizeof(*reth);
|
|
|
}
|
|
|
/*
|
|
|
- * If we receive a duplicate RDMA request, it means the
|
|
|
- * requester saw a sequence error and needs to restart
|
|
|
- * from an earlier point. We can abort the current
|
|
|
- * RDMA read send in that case.
|
|
|
+ * Address range must be a subset of the original
|
|
|
+ * request and start on pmtu boundaries.
|
|
|
+ * We reuse the old ack_queue slot since the requester
|
|
|
+ * should not back up and request an earlier PSN for the
|
|
|
+ * same request.
|
|
|
*/
|
|
|
- spin_lock_irq(&qp->s_lock);
|
|
|
- if (qp->s_ack_state != OP(ACKNOWLEDGE) &&
|
|
|
- (qp->s_hdrwords || ipath_cmp24(psn, qp->s_ack_psn) >= 0)) {
|
|
|
- /*
|
|
|
- * We are already sending earlier requested data.
|
|
|
- * Don't abort it to send later out of sequence data.
|
|
|
- */
|
|
|
- spin_unlock_irq(&qp->s_lock);
|
|
|
- goto done;
|
|
|
- }
|
|
|
- qp->s_rdma_len = be32_to_cpu(reth->length);
|
|
|
- if (qp->s_rdma_len != 0) {
|
|
|
+ offset = ((psn - e->psn) & IPATH_PSN_MASK) *
|
|
|
+ ib_mtu_enum_to_int(qp->path_mtu);
|
|
|
+ len = be32_to_cpu(reth->length);
|
|
|
+ if (unlikely(offset + len > e->rdma_sge.sge.sge_length))
|
|
|
+ goto unlock_done;
|
|
|
+ if (len != 0) {
|
|
|
u32 rkey = be32_to_cpu(reth->rkey);
|
|
|
u64 vaddr = be64_to_cpu(reth->vaddr);
|
|
|
int ok;
|
|
|
|
|
|
- /*
|
|
|
- * Address range must be a subset of the original
|
|
|
- * request and start on pmtu boundaries.
|
|
|
- */
|
|
|
- ok = ipath_rkey_ok(qp, &qp->s_rdma_sge,
|
|
|
- qp->s_rdma_len, vaddr, rkey,
|
|
|
+ ok = ipath_rkey_ok(qp, &e->rdma_sge,
|
|
|
+ len, vaddr, rkey,
|
|
|
IB_ACCESS_REMOTE_READ);
|
|
|
- if (unlikely(!ok)) {
|
|
|
- spin_unlock_irq(&qp->s_lock);
|
|
|
- goto done;
|
|
|
- }
|
|
|
+ if (unlikely(!ok))
|
|
|
+ goto unlock_done;
|
|
|
} else {
|
|
|
- qp->s_rdma_sge.sg_list = NULL;
|
|
|
- qp->s_rdma_sge.num_sge = 0;
|
|
|
- qp->s_rdma_sge.sge.mr = NULL;
|
|
|
- qp->s_rdma_sge.sge.vaddr = NULL;
|
|
|
- qp->s_rdma_sge.sge.length = 0;
|
|
|
- qp->s_rdma_sge.sge.sge_length = 0;
|
|
|
+ e->rdma_sge.sg_list = NULL;
|
|
|
+ e->rdma_sge.num_sge = 0;
|
|
|
+ e->rdma_sge.sge.mr = NULL;
|
|
|
+ e->rdma_sge.sge.vaddr = NULL;
|
|
|
+ e->rdma_sge.sge.length = 0;
|
|
|
+ e->rdma_sge.sge.sge_length = 0;
|
|
|
}
|
|
|
- qp->s_ack_state = opcode;
|
|
|
- qp->s_ack_psn = psn;
|
|
|
- spin_unlock_irq(&qp->s_lock);
|
|
|
- tasklet_hi_schedule(&qp->s_task);
|
|
|
- goto send_ack;
|
|
|
+ e->psn = psn;
|
|
|
+ qp->s_ack_state = OP(ACKNOWLEDGE);
|
|
|
+ qp->s_tail_ack_queue = prev;
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * A pending RDMA read will ACK anything before it so
|
|
|
- * ignore earlier duplicate requests.
|
|
|
- */
|
|
|
- if (qp->s_ack_state != OP(ACKNOWLEDGE))
|
|
|
- goto done;
|
|
|
-
|
|
|
- /*
|
|
|
- * If an ACK is pending, don't replace the pending ACK
|
|
|
- * with an earlier one since the later one will ACK the earlier.
|
|
|
- * Also, if we already have a pending atomic, send it.
|
|
|
- */
|
|
|
- if (qp->r_ack_state != OP(ACKNOWLEDGE) &&
|
|
|
- (ipath_cmp24(psn, qp->r_ack_psn) <= 0 ||
|
|
|
- qp->r_ack_state >= OP(COMPARE_SWAP)))
|
|
|
- goto send_ack;
|
|
|
- switch (opcode) {
|
|
|
case OP(COMPARE_SWAP):
|
|
|
- case OP(FETCH_ADD):
|
|
|
+ case OP(FETCH_ADD): {
|
|
|
/*
|
|
|
- * Check for the PSN of the last atomic operation
|
|
|
- * performed and resend the result if found.
|
|
|
+ * If we didn't find the atomic request in the ack queue
|
|
|
+ * or the send tasklet is already backed up to send an
|
|
|
+ * earlier entry, we can ignore this request.
|
|
|
*/
|
|
|
- if ((psn & IPATH_PSN_MASK) != qp->r_atomic_psn)
|
|
|
- goto done;
|
|
|
+ if (!e || e->opcode != (u8) opcode || old_req)
|
|
|
+ goto unlock_done;
|
|
|
+ qp->s_ack_state = OP(ACKNOWLEDGE);
|
|
|
+ qp->s_tail_ack_queue = prev;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ default:
|
|
|
+ if (old_req)
|
|
|
+ goto unlock_done;
|
|
|
+ /*
|
|
|
+ * Resend the most recent ACK if this request is
|
|
|
+ * after all the previous RDMA reads and atomics.
|
|
|
+ */
|
|
|
+ if (i == qp->r_head_ack_queue) {
|
|
|
+ spin_unlock_irq(&qp->s_lock);
|
|
|
+ qp->r_nak_state = 0;
|
|
|
+ qp->r_ack_psn = qp->r_psn - 1;
|
|
|
+ goto send_ack;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * Resend the RDMA read or atomic op which
|
|
|
+ * ACKs this duplicate request.
|
|
|
+ */
|
|
|
+ qp->s_ack_state = OP(ACKNOWLEDGE);
|
|
|
+ qp->s_tail_ack_queue = i;
|
|
|
break;
|
|
|
}
|
|
|
- qp->r_ack_state = opcode;
|
|
|
qp->r_nak_state = 0;
|
|
|
- qp->r_ack_psn = psn;
|
|
|
-send_ack:
|
|
|
- return 0;
|
|
|
+ spin_unlock_irq(&qp->s_lock);
|
|
|
+ tasklet_hi_schedule(&qp->s_task);
|
|
|
|
|
|
+unlock_done:
|
|
|
+ spin_unlock_irq(&qp->s_lock);
|
|
|
done:
|
|
|
return 1;
|
|
|
+
|
|
|
+send_ack:
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static void ipath_rc_error(struct ipath_qp *qp, enum ib_wc_status err)
|
|
@@ -1391,15 +1506,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
|
|
|
opcode == OP(SEND_LAST_WITH_IMMEDIATE))
|
|
|
break;
|
|
|
nack_inv:
|
|
|
- /*
|
|
|
- * A NAK will ACK earlier sends and RDMA writes.
|
|
|
- * Don't queue the NAK if a RDMA read, atomic, or NAK
|
|
|
- * is pending though.
|
|
|
- */
|
|
|
- if (qp->r_ack_state >= OP(COMPARE_SWAP))
|
|
|
- goto send_ack;
|
|
|
ipath_rc_error(qp, IB_WC_REM_INV_REQ_ERR);
|
|
|
- qp->r_ack_state = OP(SEND_ONLY);
|
|
|
qp->r_nak_state = IB_NAK_INVALID_REQUEST;
|
|
|
qp->r_ack_psn = qp->r_psn;
|
|
|
goto send_ack;
|
|
@@ -1441,9 +1548,8 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
|
|
|
* Don't queue the NAK if a RDMA read or atomic
|
|
|
* is pending though.
|
|
|
*/
|
|
|
- if (qp->r_ack_state >= OP(COMPARE_SWAP))
|
|
|
- goto send_ack;
|
|
|
- qp->r_ack_state = OP(SEND_ONLY);
|
|
|
+ if (qp->r_nak_state)
|
|
|
+ goto done;
|
|
|
qp->r_nak_state = IB_RNR_NAK | qp->r_min_rnr_timer;
|
|
|
qp->r_ack_psn = qp->r_psn;
|
|
|
goto send_ack;
|
|
@@ -1567,7 +1673,19 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
|
|
|
goto rnr_nak;
|
|
|
goto send_last_imm;
|
|
|
|
|
|
- case OP(RDMA_READ_REQUEST):
|
|
|
+ case OP(RDMA_READ_REQUEST): {
|
|
|
+ struct ipath_ack_entry *e;
|
|
|
+ u32 len;
|
|
|
+ u8 next;
|
|
|
+
|
|
|
+ if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_READ)))
|
|
|
+ goto nack_acc;
|
|
|
+ next = qp->r_head_ack_queue + 1;
|
|
|
+ if (next > IPATH_MAX_RDMA_ATOMIC)
|
|
|
+ next = 0;
|
|
|
+ if (unlikely(next == qp->s_tail_ack_queue))
|
|
|
+ goto nack_inv;
|
|
|
+ e = &qp->s_ack_queue[qp->r_head_ack_queue];
|
|
|
/* RETH comes after BTH */
|
|
|
if (!header_in_data)
|
|
|
reth = &ohdr->u.rc.reth;
|
|
@@ -1575,72 +1693,75 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
|
|
|
reth = (struct ib_reth *)data;
|
|
|
data += sizeof(*reth);
|
|
|
}
|
|
|
- if (unlikely(!(qp->qp_access_flags &
|
|
|
- IB_ACCESS_REMOTE_READ)))
|
|
|
- goto nack_acc;
|
|
|
- spin_lock_irq(&qp->s_lock);
|
|
|
- qp->s_rdma_len = be32_to_cpu(reth->length);
|
|
|
- if (qp->s_rdma_len != 0) {
|
|
|
+ len = be32_to_cpu(reth->length);
|
|
|
+ if (len) {
|
|
|
u32 rkey = be32_to_cpu(reth->rkey);
|
|
|
u64 vaddr = be64_to_cpu(reth->vaddr);
|
|
|
int ok;
|
|
|
|
|
|
/* Check rkey & NAK */
|
|
|
- ok = ipath_rkey_ok(qp, &qp->s_rdma_sge,
|
|
|
- qp->s_rdma_len, vaddr, rkey,
|
|
|
- IB_ACCESS_REMOTE_READ);
|
|
|
- if (unlikely(!ok)) {
|
|
|
- spin_unlock_irq(&qp->s_lock);
|
|
|
+ ok = ipath_rkey_ok(qp, &e->rdma_sge, len, vaddr,
|
|
|
+ rkey, IB_ACCESS_REMOTE_READ);
|
|
|
+ if (unlikely(!ok))
|
|
|
goto nack_acc;
|
|
|
- }
|
|
|
/*
|
|
|
* Update the next expected PSN. We add 1 later
|
|
|
* below, so only add the remainder here.
|
|
|
*/
|
|
|
- if (qp->s_rdma_len > pmtu)
|
|
|
- qp->r_psn += (qp->s_rdma_len - 1) / pmtu;
|
|
|
+ if (len > pmtu)
|
|
|
+ qp->r_psn += (len - 1) / pmtu;
|
|
|
} else {
|
|
|
- qp->s_rdma_sge.sg_list = NULL;
|
|
|
- qp->s_rdma_sge.num_sge = 0;
|
|
|
- qp->s_rdma_sge.sge.mr = NULL;
|
|
|
- qp->s_rdma_sge.sge.vaddr = NULL;
|
|
|
- qp->s_rdma_sge.sge.length = 0;
|
|
|
- qp->s_rdma_sge.sge.sge_length = 0;
|
|
|
+ e->rdma_sge.sg_list = NULL;
|
|
|
+ e->rdma_sge.num_sge = 0;
|
|
|
+ e->rdma_sge.sge.mr = NULL;
|
|
|
+ e->rdma_sge.sge.vaddr = NULL;
|
|
|
+ e->rdma_sge.sge.length = 0;
|
|
|
+ e->rdma_sge.sge.sge_length = 0;
|
|
|
}
|
|
|
+ e->opcode = opcode;
|
|
|
+ e->psn = psn;
|
|
|
/*
|
|
|
* We need to increment the MSN here instead of when we
|
|
|
* finish sending the result since a duplicate request would
|
|
|
* increment it more than once.
|
|
|
*/
|
|
|
qp->r_msn++;
|
|
|
-
|
|
|
- qp->s_ack_state = opcode;
|
|
|
- qp->s_ack_psn = psn;
|
|
|
- spin_unlock_irq(&qp->s_lock);
|
|
|
-
|
|
|
qp->r_psn++;
|
|
|
qp->r_state = opcode;
|
|
|
qp->r_nak_state = 0;
|
|
|
+ barrier();
|
|
|
+ qp->r_head_ack_queue = next;
|
|
|
|
|
|
/* Call ipath_do_rc_send() in another thread. */
|
|
|
tasklet_hi_schedule(&qp->s_task);
|
|
|
|
|
|
goto done;
|
|
|
+ }
|
|
|
|
|
|
case OP(COMPARE_SWAP):
|
|
|
case OP(FETCH_ADD): {
|
|
|
struct ib_atomic_eth *ateth;
|
|
|
+ struct ipath_ack_entry *e;
|
|
|
u64 vaddr;
|
|
|
+ atomic64_t *maddr;
|
|
|
u64 sdata;
|
|
|
u32 rkey;
|
|
|
+ u8 next;
|
|
|
|
|
|
+ if (unlikely(!(qp->qp_access_flags &
|
|
|
+ IB_ACCESS_REMOTE_ATOMIC)))
|
|
|
+ goto nack_acc;
|
|
|
+ next = qp->r_head_ack_queue + 1;
|
|
|
+ if (next > IPATH_MAX_RDMA_ATOMIC)
|
|
|
+ next = 0;
|
|
|
+ if (unlikely(next == qp->s_tail_ack_queue))
|
|
|
+ goto nack_inv;
|
|
|
if (!header_in_data)
|
|
|
ateth = &ohdr->u.atomic_eth;
|
|
|
- else {
|
|
|
+ else
|
|
|
ateth = (struct ib_atomic_eth *)data;
|
|
|
- data += sizeof(*ateth);
|
|
|
- }
|
|
|
- vaddr = be64_to_cpu(ateth->vaddr);
|
|
|
+ vaddr = ((u64) be32_to_cpu(ateth->vaddr[0]) << 32) |
|
|
|
+ be32_to_cpu(ateth->vaddr[1]);
|
|
|
if (unlikely(vaddr & (sizeof(u64) - 1)))
|
|
|
goto nack_inv;
|
|
|
rkey = be32_to_cpu(ateth->rkey);
|
|
@@ -1649,63 +1770,50 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
|
|
|
sizeof(u64), vaddr, rkey,
|
|
|
IB_ACCESS_REMOTE_ATOMIC)))
|
|
|
goto nack_acc;
|
|
|
- if (unlikely(!(qp->qp_access_flags &
|
|
|
- IB_ACCESS_REMOTE_ATOMIC)))
|
|
|
- goto nack_acc;
|
|
|
/* Perform atomic OP and save result. */
|
|
|
+ maddr = (atomic64_t *) qp->r_sge.sge.vaddr;
|
|
|
sdata = be64_to_cpu(ateth->swap_data);
|
|
|
- spin_lock_irq(&dev->pending_lock);
|
|
|
- qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr;
|
|
|
- if (opcode == OP(FETCH_ADD))
|
|
|
- *(u64 *) qp->r_sge.sge.vaddr =
|
|
|
- qp->r_atomic_data + sdata;
|
|
|
- else if (qp->r_atomic_data ==
|
|
|
- be64_to_cpu(ateth->compare_data))
|
|
|
- *(u64 *) qp->r_sge.sge.vaddr = sdata;
|
|
|
- spin_unlock_irq(&dev->pending_lock);
|
|
|
+ e = &qp->s_ack_queue[qp->r_head_ack_queue];
|
|
|
+ e->atomic_data = (opcode == OP(FETCH_ADD)) ?
|
|
|
+ (u64) atomic64_add_return(sdata, maddr) - sdata :
|
|
|
+ (u64) cmpxchg((u64 *) qp->r_sge.sge.vaddr,
|
|
|
+ be64_to_cpu(ateth->compare_data),
|
|
|
+ sdata);
|
|
|
+ e->opcode = opcode;
|
|
|
+ e->psn = psn & IPATH_PSN_MASK;
|
|
|
qp->r_msn++;
|
|
|
- qp->r_atomic_psn = psn & IPATH_PSN_MASK;
|
|
|
- psn |= 1 << 31;
|
|
|
- break;
|
|
|
+ qp->r_psn++;
|
|
|
+ qp->r_state = opcode;
|
|
|
+ qp->r_nak_state = 0;
|
|
|
+ barrier();
|
|
|
+ qp->r_head_ack_queue = next;
|
|
|
+
|
|
|
+ /* Call ipath_do_rc_send() in another thread. */
|
|
|
+ tasklet_hi_schedule(&qp->s_task);
|
|
|
+
|
|
|
+ goto done;
|
|
|
}
|
|
|
|
|
|
default:
|
|
|
- /* Drop packet for unknown opcodes. */
|
|
|
- goto done;
|
|
|
+ /* NAK unknown opcodes. */
|
|
|
+ goto nack_inv;
|
|
|
}
|
|
|
qp->r_psn++;
|
|
|
qp->r_state = opcode;
|
|
|
+ qp->r_ack_psn = psn;
|
|
|
qp->r_nak_state = 0;
|
|
|
/* Send an ACK if requested or required. */
|
|
|
- if (psn & (1 << 31)) {
|
|
|
- /*
|
|
|
- * Coalesce ACKs unless there is a RDMA READ or
|
|
|
- * ATOMIC pending.
|
|
|
- */
|
|
|
- if (qp->r_ack_state < OP(COMPARE_SWAP)) {
|
|
|
- qp->r_ack_state = opcode;
|
|
|
- qp->r_ack_psn = psn;
|
|
|
- }
|
|
|
+ if (psn & (1 << 31))
|
|
|
goto send_ack;
|
|
|
- }
|
|
|
goto done;
|
|
|
|
|
|
nack_acc:
|
|
|
- /*
|
|
|
- * A NAK will ACK earlier sends and RDMA writes.
|
|
|
- * Don't queue the NAK if a RDMA read, atomic, or NAK
|
|
|
- * is pending though.
|
|
|
- */
|
|
|
- if (qp->r_ack_state < OP(COMPARE_SWAP)) {
|
|
|
- ipath_rc_error(qp, IB_WC_REM_ACCESS_ERR);
|
|
|
- qp->r_ack_state = OP(RDMA_WRITE_ONLY);
|
|
|
- qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR;
|
|
|
- qp->r_ack_psn = qp->r_psn;
|
|
|
- }
|
|
|
+ ipath_rc_error(qp, IB_WC_REM_ACCESS_ERR);
|
|
|
+ qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR;
|
|
|
+ qp->r_ack_psn = qp->r_psn;
|
|
|
+
|
|
|
send_ack:
|
|
|
- /* Send ACK right away unless the send tasklet has a pending ACK. */
|
|
|
- if (qp->s_ack_state == OP(ACKNOWLEDGE))
|
|
|
- send_rc_ack(qp);
|
|
|
+ send_rc_ack(qp);
|
|
|
|
|
|
done:
|
|
|
return;
|