diff options
author | Ralph Campbell <ralph.campbell@qlogic.com> | 2007-06-18 17:24:44 -0400 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2007-07-09 23:12:26 -0400 |
commit | d781b129f1e8b3e2f369d8035a61a5233832e65c (patch) | |
tree | 7ff0bfbcd2e32a4693fa2007b773a7ca87968464 | |
parent | 30d149ab58cc3ed8e4bc9c4dc45bebbed0e84b6e (diff) |
IB/ipath: Duplicate RDMA reads can cause responder to NAK inappropriately
A duplicate RDMA read request can fool the responder into NAKing a new
RDMA read request because the responder wasn't keeping track of
whether the queue of RDMA read requests had been sent at least once.
For example, requester sends 4 2K byte RDMA read requests, times out,
and resends the first, then sees the 4 responses, then sends a 5th
RDMA read or atomic operation. The responder sees the 4 requests,
sends 4 responses, sees the resent 1st request, rewinds the queue,
then sees the 5th request but thinks the queue is full and that the
requester is invalidly sending a 5th new request.
Signed-off-by: Ralph Campbell <ralph.campbell@qlogic.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
-rw-r--r-- | drivers/infiniband/hw/ipath/ipath_rc.c | 38 | ||||
-rw-r--r-- | drivers/infiniband/hw/ipath/ipath_verbs.h | 1 |
2 files changed, 34 insertions, 5 deletions
diff --git a/drivers/infiniband/hw/ipath/ipath_rc.c b/drivers/infiniband/hw/ipath/ipath_rc.c index 014d811d222d..9e7123987ae6 100644 --- a/drivers/infiniband/hw/ipath/ipath_rc.c +++ b/drivers/infiniband/hw/ipath/ipath_rc.c | |||
@@ -125,8 +125,10 @@ static int ipath_make_rc_ack(struct ipath_qp *qp, | |||
125 | if (len > pmtu) { | 125 | if (len > pmtu) { |
126 | len = pmtu; | 126 | len = pmtu; |
127 | qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST); | 127 | qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST); |
128 | } else | 128 | } else { |
129 | qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY); | 129 | qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY); |
130 | e->sent = 1; | ||
131 | } | ||
130 | ohdr->u.aeth = ipath_compute_aeth(qp); | 132 | ohdr->u.aeth = ipath_compute_aeth(qp); |
131 | hwords++; | 133 | hwords++; |
132 | qp->s_ack_rdma_psn = e->psn; | 134 | qp->s_ack_rdma_psn = e->psn; |
@@ -143,6 +145,7 @@ static int ipath_make_rc_ack(struct ipath_qp *qp, | |||
143 | cpu_to_be32(e->atomic_data); | 145 | cpu_to_be32(e->atomic_data); |
144 | hwords += sizeof(ohdr->u.at) / sizeof(u32); | 146 | hwords += sizeof(ohdr->u.at) / sizeof(u32); |
145 | bth2 = e->psn; | 147 | bth2 = e->psn; |
148 | e->sent = 1; | ||
146 | } | 149 | } |
147 | bth0 = qp->s_ack_state << 24; | 150 | bth0 = qp->s_ack_state << 24; |
148 | break; | 151 | break; |
@@ -158,6 +161,7 @@ static int ipath_make_rc_ack(struct ipath_qp *qp, | |||
158 | ohdr->u.aeth = ipath_compute_aeth(qp); | 161 | ohdr->u.aeth = ipath_compute_aeth(qp); |
159 | hwords++; | 162 | hwords++; |
160 | qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); | 163 | qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); |
164 | qp->s_ack_queue[qp->s_tail_ack_queue].sent = 1; | ||
161 | } | 165 | } |
162 | bth0 = qp->s_ack_state << 24; | 166 | bth0 = qp->s_ack_state << 24; |
163 | bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK; | 167 | bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK; |
@@ -1479,6 +1483,22 @@ static void ipath_rc_error(struct ipath_qp *qp, enum ib_wc_status err) | |||
1479 | spin_unlock_irqrestore(&qp->s_lock, flags); | 1483 | spin_unlock_irqrestore(&qp->s_lock, flags); |
1480 | } | 1484 | } |
1481 | 1485 | ||
1486 | static inline void ipath_update_ack_queue(struct ipath_qp *qp, unsigned n) | ||
1487 | { | ||
1488 | unsigned long flags; | ||
1489 | unsigned next; | ||
1490 | |||
1491 | next = n + 1; | ||
1492 | if (next > IPATH_MAX_RDMA_ATOMIC) | ||
1493 | next = 0; | ||
1494 | spin_lock_irqsave(&qp->s_lock, flags); | ||
1495 | if (n == qp->s_tail_ack_queue) { | ||
1496 | qp->s_tail_ack_queue = next; | ||
1497 | qp->s_ack_state = OP(ACKNOWLEDGE); | ||
1498 | } | ||
1499 | spin_unlock_irqrestore(&qp->s_lock, flags); | ||
1500 | } | ||
1501 | |||
1482 | /** | 1502 | /** |
1483 | * ipath_rc_rcv - process an incoming RC packet | 1503 | * ipath_rc_rcv - process an incoming RC packet |
1484 | * @dev: the device this packet came in on | 1504 | * @dev: the device this packet came in on |
@@ -1741,8 +1761,11 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, | |||
1741 | next = qp->r_head_ack_queue + 1; | 1761 | next = qp->r_head_ack_queue + 1; |
1742 | if (next > IPATH_MAX_RDMA_ATOMIC) | 1762 | if (next > IPATH_MAX_RDMA_ATOMIC) |
1743 | next = 0; | 1763 | next = 0; |
1744 | if (unlikely(next == qp->s_tail_ack_queue)) | 1764 | if (unlikely(next == qp->s_tail_ack_queue)) { |
1745 | goto nack_inv; | 1765 | if (!qp->s_ack_queue[next].sent) |
1766 | goto nack_inv; | ||
1767 | ipath_update_ack_queue(qp, next); | ||
1768 | } | ||
1746 | e = &qp->s_ack_queue[qp->r_head_ack_queue]; | 1769 | e = &qp->s_ack_queue[qp->r_head_ack_queue]; |
1747 | /* RETH comes after BTH */ | 1770 | /* RETH comes after BTH */ |
1748 | if (!header_in_data) | 1771 | if (!header_in_data) |
@@ -1777,6 +1800,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, | |||
1777 | e->rdma_sge.sge.sge_length = 0; | 1800 | e->rdma_sge.sge.sge_length = 0; |
1778 | } | 1801 | } |
1779 | e->opcode = opcode; | 1802 | e->opcode = opcode; |
1803 | e->sent = 0; | ||
1780 | e->psn = psn; | 1804 | e->psn = psn; |
1781 | /* | 1805 | /* |
1782 | * We need to increment the MSN here instead of when we | 1806 | * We need to increment the MSN here instead of when we |
@@ -1812,8 +1836,11 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, | |||
1812 | next = qp->r_head_ack_queue + 1; | 1836 | next = qp->r_head_ack_queue + 1; |
1813 | if (next > IPATH_MAX_RDMA_ATOMIC) | 1837 | if (next > IPATH_MAX_RDMA_ATOMIC) |
1814 | next = 0; | 1838 | next = 0; |
1815 | if (unlikely(next == qp->s_tail_ack_queue)) | 1839 | if (unlikely(next == qp->s_tail_ack_queue)) { |
1816 | goto nack_inv; | 1840 | if (!qp->s_ack_queue[next].sent) |
1841 | goto nack_inv; | ||
1842 | ipath_update_ack_queue(qp, next); | ||
1843 | } | ||
1817 | if (!header_in_data) | 1844 | if (!header_in_data) |
1818 | ateth = &ohdr->u.atomic_eth; | 1845 | ateth = &ohdr->u.atomic_eth; |
1819 | else | 1846 | else |
@@ -1838,6 +1865,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, | |||
1838 | be64_to_cpu(ateth->compare_data), | 1865 | be64_to_cpu(ateth->compare_data), |
1839 | sdata); | 1866 | sdata); |
1840 | e->opcode = opcode; | 1867 | e->opcode = opcode; |
1868 | e->sent = 0; | ||
1841 | e->psn = psn & IPATH_PSN_MASK; | 1869 | e->psn = psn & IPATH_PSN_MASK; |
1842 | qp->r_msn++; | 1870 | qp->r_msn++; |
1843 | qp->r_psn++; | 1871 | qp->r_psn++; |
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h index 088b837ebea8..458f8227fa36 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.h +++ b/drivers/infiniband/hw/ipath/ipath_verbs.h | |||
@@ -321,6 +321,7 @@ struct ipath_sge_state { | |||
321 | */ | 321 | */ |
322 | struct ipath_ack_entry { | 322 | struct ipath_ack_entry { |
323 | u8 opcode; | 323 | u8 opcode; |
324 | u8 sent; | ||
324 | u32 psn; | 325 | u32 psn; |
325 | union { | 326 | union { |
326 | struct ipath_sge_state rdma_sge; | 327 | struct ipath_sge_state rdma_sge; |