Skip to content

fix(client): Avoid missing final send CQE - #133

Open
nchild-cornelis-networks wants to merge 1 commit into
ThinkParQ:masterfrom
nchild-cornelis-networks:nchild/missing-cqe
Open

nchild-cornelis-networks wants to merge 1 commit into
ThinkParQ:masterfrom
nchild-cornelis-networks:nchild/missing-cqe

Conversation

@nchild-cornelis-networks

Copy link
Copy Markdown

Hey all!

So we kept hitting odd timeouts on our small RDMA deployment. We would run elbencho to get a filesystem benchmark and every 50 or so runs the ~10s test would take 5 minutes and eventually result in some messages like:

beegfs: elb-wrk-6(163505): writefile (communication): Failed to send message to node_storage_1
beegfs: elb-wrk-6(163505): writefile (communication): SocketError. ErrCode: -70
beegfs: elb-wrk-6(163505): writefile (communication): Communication error in SENDDATA stage. Node: node_storage_2
beegfs: elb-wrk-6(163505): writefile (communication): Communication error. Node: node_storage_1
beegfs: elb-wrk-6(163505): writefile (communication): Communication error. Node: node_storage_2
beegfs: elb-wrk-1(163500): writefile (communication): Failed to send message to node_storage_1
beegfs: elb-wrk-1(163500): writefile (communication): Communication error. Node: node_storage_1

After much bpftracing, the finding was that there was a CQE just waiting in the CQ with no one polling it for the full timeout window. At the time the CQE was enqueued, IB_CQ_NEXT_COMP was not set but it was set sometime after the enqueue..

From RDMA providers perspective everything is valid. This is the exact race that IB_CQ_REPORT_MISSED_EVENTS was added to catch. Beegfs does not use IB_CQ_REPORT_MISSED_EVENTS so this is not caught.

Option 1 is to add beegfs support for IB_CQ_REPORT_MISSED_EVENTS. I chose not to do this because I do not know if other vendors support this and that would require more code overhaul than just the simple two line change.

In the end I could avoid the issue by swapping the ordering of the atomic inc and the req_notify call. This makes sense to me because we don't want to notify about a new CQE UNTIL we are re-armed to capture the next CQE, otherwise there IS a race window and we may miss the next CQE, perhaps I explain this better in the commit message. But essentially, the pseudocode of previous implementation and order of execution was the following:

__IBVSocket_sendCompletionHandler:
     atomic_inc(cnt)                 <- 1. CQE X callback we bump
     ib_req_notify_cq()             <- 5. now set notify bit


__IBVSocket_waitForTotalSendCompletion:
  do {
    saved_cnt = atomic_read(cnt)  <-  2. save after CQE X bumps
    if (ib_poll_cq() = 0)                    <- 3. empty poll, we polled X in prev iteration 
        wait_until  atomic_read(cnt) != saved_cnt  <- 6. wait for CQE which is already enqueued
  } while (outstanding ops)

<RDMA vendor enqueues new CQE>    <- 4. notify bit is not set so silently enqueue

Swap the order of the atomic_inc and the ib_req_notify_cq when in a send
CQ notify callback. Otherwise, there is a possible race with polling
the last CQE and re-arming the IB_CQ_NEXT_COMP flag.

For example, previously the following series of events was possible:
1. In polling thread, CQE X is polled off the queue
2. In the notify callback thread, the notify for X arrives, it
   increments the atomic
3. In polling thread: capture atomic, do empty poll, start waiting
4. In RDMA provider thread: Enqueue CQE Y, IB_CQ_NEXT_COMP is not yet set,
   so don't issue a notify callback
5. In X's notify callback thread, we issue ib_req_notify_cq and wake
6. In poll thread; Is awoken but the waiter's predicate is false,
   sendCompEventCount is unchanged from step 3, so it sleeps again.
7. Wait with notify armed and and an entry waiting to be polled in the CQE.
   Eventually timeout.

This is a very tight race window but is rather common if the RDMA provider
serializes the CQ enqueue, poll, and notify operations (which OPA
does).

The alternative and proper fix is to implement
IB_CQ_REPORT_MISSED_EVENTS, but that would require a larger code
overhaul.

Signed-off-by: Nick Child <nchild@cornelisnetworks.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant