IB/ipath: Fix PSN update for RC retries

This patch fixes a number of bugs with updating the PSN for retries of
RC requests.

Signed-off-by: Bryan O'Sullivan <bryan.osullivan@qlogic.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
This commit is contained in:
Ralph Campbell 2007-03-15 14:44:54 -07:00 committed by Roland Dreier
parent 0434d271fd
commit 6f5c407460
1 changed files with 38 additions and 27 deletions

View File

@ -444,7 +444,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
qp->s_psn = wqe->lpsn + 1; qp->s_psn = wqe->lpsn + 1;
else { else {
qp->s_psn++; qp->s_psn++;
if ((int)(qp->s_psn - qp->s_next_psn) > 0) if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
qp->s_next_psn = qp->s_psn; qp->s_next_psn = qp->s_psn;
} }
/* /*
@ -471,7 +471,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
/* FALLTHROUGH */ /* FALLTHROUGH */
case OP(SEND_MIDDLE): case OP(SEND_MIDDLE):
bth2 = qp->s_psn++ & IPATH_PSN_MASK; bth2 = qp->s_psn++ & IPATH_PSN_MASK;
if ((int)(qp->s_psn - qp->s_next_psn) > 0) if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
qp->s_next_psn = qp->s_psn; qp->s_next_psn = qp->s_psn;
ss = &qp->s_sge; ss = &qp->s_sge;
len = qp->s_len; len = qp->s_len;
@ -507,7 +507,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
/* FALLTHROUGH */ /* FALLTHROUGH */
case OP(RDMA_WRITE_MIDDLE): case OP(RDMA_WRITE_MIDDLE):
bth2 = qp->s_psn++ & IPATH_PSN_MASK; bth2 = qp->s_psn++ & IPATH_PSN_MASK;
if ((int)(qp->s_psn - qp->s_next_psn) > 0) if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
qp->s_next_psn = qp->s_psn; qp->s_next_psn = qp->s_psn;
ss = &qp->s_sge; ss = &qp->s_sge;
len = qp->s_len; len = qp->s_len;
@ -546,7 +546,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
qp->s_state = OP(RDMA_READ_REQUEST); qp->s_state = OP(RDMA_READ_REQUEST);
hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32); hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32);
bth2 = qp->s_psn++ & IPATH_PSN_MASK; bth2 = qp->s_psn++ & IPATH_PSN_MASK;
if ((int)(qp->s_psn - qp->s_next_psn) > 0) if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0)
qp->s_next_psn = qp->s_psn; qp->s_next_psn = qp->s_psn;
ss = NULL; ss = NULL;
len = 0; len = 0;
@ -779,7 +779,7 @@ void ipath_restart_rc(struct ipath_qp *qp, u32 psn, struct ib_wc *wc)
if (wqe->wr.opcode == IB_WR_RDMA_READ) if (wqe->wr.opcode == IB_WR_RDMA_READ)
dev->n_rc_resends++; dev->n_rc_resends++;
else else
dev->n_rc_resends += (int)qp->s_psn - (int)psn; dev->n_rc_resends += (qp->s_psn - psn) & IPATH_PSN_MASK;
reset_psn(qp, psn); reset_psn(qp, psn);
tasklet_hi_schedule(&qp->s_task); tasklet_hi_schedule(&qp->s_task);
@ -915,15 +915,19 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
if (qp->s_last == qp->s_cur) { if (qp->s_last == qp->s_cur) {
if (++qp->s_cur >= qp->s_size) if (++qp->s_cur >= qp->s_size)
qp->s_cur = 0; qp->s_cur = 0;
qp->s_last = qp->s_cur;
if (qp->s_last == qp->s_tail)
break;
wqe = get_swqe_ptr(qp, qp->s_cur); wqe = get_swqe_ptr(qp, qp->s_cur);
qp->s_state = OP(SEND_LAST); qp->s_state = OP(SEND_LAST);
qp->s_psn = wqe->psn; qp->s_psn = wqe->psn;
} else {
if (++qp->s_last >= qp->s_size)
qp->s_last = 0;
if (qp->s_last == qp->s_tail)
break;
wqe = get_swqe_ptr(qp, qp->s_last);
} }
if (++qp->s_last >= qp->s_size)
qp->s_last = 0;
wqe = get_swqe_ptr(qp, qp->s_last);
if (qp->s_last == qp->s_tail)
break;
} }
switch (aeth >> 29) { switch (aeth >> 29) {
@ -935,6 +939,18 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
list_add_tail(&qp->timerwait, list_add_tail(&qp->timerwait,
&dev->pending[dev->pending_index]); &dev->pending[dev->pending_index]);
spin_unlock(&dev->pending_lock); spin_unlock(&dev->pending_lock);
/*
* If we get a partial ACK for a resent operation,
* we can stop resending the earlier packets and
* continue with the next packet the receiver wants.
*/
if (ipath_cmp24(qp->s_psn, psn) <= 0) {
reset_psn(qp, psn + 1);
tasklet_hi_schedule(&qp->s_task);
}
} else if (ipath_cmp24(qp->s_psn, psn) <= 0) {
qp->s_state = OP(SEND_LAST);
qp->s_psn = psn + 1;
} }
ipath_get_credit(qp, aeth); ipath_get_credit(qp, aeth);
qp->s_rnr_retry = qp->s_rnr_retry_cnt; qp->s_rnr_retry = qp->s_rnr_retry_cnt;
@ -945,22 +961,23 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
case 1: /* RNR NAK */ case 1: /* RNR NAK */
dev->n_rnr_naks++; dev->n_rnr_naks++;
if (qp->s_last == qp->s_tail)
goto bail;
if (qp->s_rnr_retry == 0) { if (qp->s_rnr_retry == 0) {
if (qp->s_last == qp->s_tail)
goto bail;
wc.status = IB_WC_RNR_RETRY_EXC_ERR; wc.status = IB_WC_RNR_RETRY_EXC_ERR;
goto class_b; goto class_b;
} }
if (qp->s_rnr_retry_cnt < 7) if (qp->s_rnr_retry_cnt < 7)
qp->s_rnr_retry--; qp->s_rnr_retry--;
if (qp->s_last == qp->s_tail)
goto bail;
/* The last valid PSN is the previous PSN. */ /* The last valid PSN is the previous PSN. */
update_last_psn(qp, psn - 1); update_last_psn(qp, psn - 1);
dev->n_rc_resends += (int)qp->s_psn - (int)psn; if (wqe->wr.opcode == IB_WR_RDMA_READ)
dev->n_rc_resends++;
else
dev->n_rc_resends +=
(qp->s_psn - psn) & IPATH_PSN_MASK;
reset_psn(qp, psn); reset_psn(qp, psn);
@ -971,26 +988,20 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
goto bail; goto bail;
case 3: /* NAK */ case 3: /* NAK */
/* The last valid PSN seen is the previous request's. */ if (qp->s_last == qp->s_tail)
if (qp->s_last != qp->s_tail) goto bail;
update_last_psn(qp, wqe->psn - 1); /* The last valid PSN is the previous PSN. */
update_last_psn(qp, psn - 1);
switch ((aeth >> IPATH_AETH_CREDIT_SHIFT) & switch ((aeth >> IPATH_AETH_CREDIT_SHIFT) &
IPATH_AETH_CREDIT_MASK) { IPATH_AETH_CREDIT_MASK) {
case 0: /* PSN sequence error */ case 0: /* PSN sequence error */
dev->n_seq_naks++; dev->n_seq_naks++;
/* /*
* Back up to the responder's expected PSN. XXX * Back up to the responder's expected PSN.
* Note that we might get a NAK in the middle of an * Note that we might get a NAK in the middle of an
* RDMA READ response which terminates the RDMA * RDMA READ response which terminates the RDMA
* READ. * READ.
*/ */
if (qp->s_last == qp->s_tail)
break;
if (ipath_cmp24(psn, wqe->psn) < 0)
break;
/* Retry the request. */
ipath_restart_rc(qp, psn, &wc); ipath_restart_rc(qp, psn, &wc);
break; break;