Skip to content

Commit 0fd7e1d

Browse files
author
Roland Dreier
committed
IB/mlx4: Fix memory ordering problem when posting LSO sends
The current work request posting code writes the LSO segment before writing any data segments. This leaves a window where the LSO segment overwrites the stamping in one cacheline that the HCA prefetches before the rest of the cacheline is filled with the correct data segments. When the HCA processes this work request, a local protection error may result. Fix this by saving the LSO header size field off and writing it only after all data segments are written. This fix is a cleaned-up version of a patch from Jack Morgenstein <[email protected]>. This fixes <https://bugs.openfabrics.org/show_bug.cgi?id=1383>. Reported-by: Jack Morgenstein <[email protected]> Signed-off-by: Roland Dreier <[email protected]>
1 parent d3b924d commit 0fd7e1d

File tree

1 file changed

+19
-9
lines changed
  • drivers/infiniband/hw/mlx4

1 file changed

+19
-9
lines changed

drivers/infiniband/hw/mlx4/qp.c

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,7 +1462,8 @@ static void __set_data_seg(struct mlx4_wqe_data_seg *dseg, struct ib_sge *sg)
14621462
}
14631463

14641464
static int build_lso_seg(struct mlx4_wqe_lso_seg *wqe, struct ib_send_wr *wr,
1465-
struct mlx4_ib_qp *qp, unsigned *lso_seg_len)
1465+
struct mlx4_ib_qp *qp, unsigned *lso_seg_len,
1466+
__be32 *lso_hdr_sz)
14661467
{
14671468
unsigned halign = ALIGN(sizeof *wqe + wr->wr.ud.hlen, 16);
14681469

@@ -1479,12 +1480,8 @@ static int build_lso_seg(struct mlx4_wqe_lso_seg *wqe, struct ib_send_wr *wr,
14791480

14801481
memcpy(wqe->header, wr->wr.ud.header, wr->wr.ud.hlen);
14811482

1482-
/* make sure LSO header is written before overwriting stamping */
1483-
wmb();
1484-
1485-
wqe->mss_hdr_size = cpu_to_be32((wr->wr.ud.mss - wr->wr.ud.hlen) << 16 |
1486-
wr->wr.ud.hlen);
1487-
1483+
*lso_hdr_sz = cpu_to_be32((wr->wr.ud.mss - wr->wr.ud.hlen) << 16 |
1484+
wr->wr.ud.hlen);
14881485
*lso_seg_len = halign;
14891486
return 0;
14901487
}
@@ -1518,13 +1515,18 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
15181515
int uninitialized_var(stamp);
15191516
int uninitialized_var(size);
15201517
unsigned uninitialized_var(seglen);
1518+
__be32 dummy;
1519+
__be32 *lso_wqe;
1520+
__be32 uninitialized_var(lso_hdr_sz);
15211521
int i;
15221522

15231523
spin_lock_irqsave(&qp->sq.lock, flags);
15241524

15251525
ind = qp->sq_next_wqe;
15261526

15271527
for (nreq = 0; wr; ++nreq, wr = wr->next) {
1528+
lso_wqe = &dummy;
1529+
15281530
if (mlx4_wq_overflow(&qp->sq, nreq, qp->ibqp.send_cq)) {
15291531
err = -ENOMEM;
15301532
*bad_wr = wr;
@@ -1606,11 +1608,12 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
16061608
size += sizeof (struct mlx4_wqe_datagram_seg) / 16;
16071609

16081610
if (wr->opcode == IB_WR_LSO) {
1609-
err = build_lso_seg(wqe, wr, qp, &seglen);
1611+
err = build_lso_seg(wqe, wr, qp, &seglen, &lso_hdr_sz);
16101612
if (unlikely(err)) {
16111613
*bad_wr = wr;
16121614
goto out;
16131615
}
1616+
lso_wqe = (__be32 *) wqe;
16141617
wqe += seglen;
16151618
size += seglen / 16;
16161619
}
@@ -1652,6 +1655,14 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
16521655
for (i = wr->num_sge - 1; i >= 0; --i, --dseg)
16531656
set_data_seg(dseg, wr->sg_list + i);
16541657

1658+
/*
1659+
* Possibly overwrite stamping in cacheline with LSO
1660+
* segment only after making sure all data segments
1661+
* are written.
1662+
*/
1663+
wmb();
1664+
*lso_wqe = lso_hdr_sz;
1665+
16551666
ctrl->fence_size = (wr->send_flags & IB_SEND_FENCE ?
16561667
MLX4_WQE_CTRL_FENCE : 0) | size;
16571668

@@ -1686,7 +1697,6 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
16861697
stamp_send_wqe(qp, stamp, size * 16);
16871698
ind = pad_wraparound(qp, ind);
16881699
}
1689-
16901700
}
16911701

16921702
out:

0 commit comments

Comments
 (0)