Skip to content

prov/shm: Intermittent MPI collective hang traced to the shm provider's connreq handshake #12542

Description

@Sbozzolo

Preface

This is the result of several days of investigation into occasional hangs in MPI collectives which led me to the shm provider in libfabric. I used a large language model to guide me through the libfabric source, and the final root-cause determination is the model's. I can't personally judge whether that root cause is correct, but my aim is to provide enough reproducible data for maintainers to make the call. I've tried to clearly separate what I measured from what the model inferred and I hope that this will be helpful in investigating this issue. I am happy to share the full diagnostic logs, the instrumentation patch, and rerun with additional logging/patches, or anything else that would help.

Summary

  • A few percent of my 384-rank (192 ranks/node × 2 nodes) Palace jobs on AWS r8g.48xlarge (ARM64) hang at the first MPI collective.
  • The hang seems much less frequent when running on 4 nodes
  • The hang is eliminated by either FI_EFA_ENABLE_SHM_TRANSFER=0 or FI_EFA_USE_SM2=1, which possibly points at the shm provider's connection setup specifically.
  • Instrumentation shows shm connection requests ("connreqs") being dropped when the peer's shared-memory region isn't yet mappable (-ENOENT), with the corresponding senders stuck forever.
  • The model attributes this to a one-shot connreq that is never retried when the drop happens; I'd appreciate maintainers' judgment on whether that's correct.

The problem

Palace is an MPI code developed by AWS to simulate quantum chips. I'm working on internal infrastructure improvements that involve running Palace simulations in containers, and I want to run with EFA.

I run containers through Apptainer, and I noticed that a few percent of my jobs on r8g.48xlarge instances stall at the first MPI collective.

Software stack

  • Containers built with Spack via this GitHub action
  • OpenMPI 5.0.10, libfabric 2.5.1, with fabrics=efa,lnx,mrail,rxm,shm,sockets,tcp,udp
  • Apptainer 1.5.2
  • I also tested OpenMPI main and libfabric 2.6 and saw no difference.

Diagnosis

I don't have a minimal reproducer. The hang needs a fresh job and only appears a few percent of the time. My debugging strategy was to submit 100 jobs trying to trigger deadlocks. Then, I instrumented Palace and finally patched libfabric to print additional stats.

What pointed me at shm in libfabric

After investigating obvious problems (e.g., a rank aborting/erroring) and trying various versions of the application/libraries, I attached gdb to the hung ranks of a stuck job. The call tree captured via gdb from a stuck rank on the head node was:

  #0  efa_rdm_cq_readfrom ()              ← spinning in libfabric
  #1  ompi_mtl_ofi_progress_no_inline ()  ← OpenMPI OFI MTL calling libfabric
  #2  opal_progress ()                    ← OpenMPI progress engine
  #3  mca_pml_cm_recv ()                  ← cm PML blocking receive
  #4  ompi_coll_base_barrier_intra_tree() ← collective algorithm
  #5  PMPI_Barrier ()                     ← the MPI call that deadlocks

And when caught inside ofi_peer_cq_read (the SHM path within efa_rdm_cq_readfrom):

  #0  smr_ep_progress ()                  ← SHM provider progress
  #1  ofi_cq_progress ()                  ← util CQ progress
  #2  ofi_peer_cq_read ()                 ← FI_PEER CQ read (SHM → EFA)
  #3  efa_rdm_cq_readfrom ()              ← EFA RDM CQ read entry
  #4  ompi_mtl_ofi_progress_no_inline ()
  #5  opal_progress ()
  #6  ompi_request_default_wait ()
  #7  ompi_coll_base_reduce_generic ()    ← reduce inside allreduce
  #8  ompi_coll_base_reduce_intra_binomial ()
  #9  ompi_coll_tuned_allreduce_intra_dec_fixed ()
  #10 mca_coll_han_comm_create_new ()     ← HAN creating sub-communicators
  #11 mca_coll_han_barrier_intra_simple ()
  #12 PMPI_Barrier ()

This pointed me to libfabric as the place to look, and to shm specifically.

To further strengthen the identification of the problematic component, I found that either of these two settings would make the deadlock go away: FI_EFA_ENABLE_SHM_TRANSFER=0 or FI_EFA_USE_SM2=1.

Separately, I also looked at EFA device hardware counters and found that they the are clean and symmetric on both nodes even when the job is stuck (rx_pkts/tx_pkts mirror each other, rx_drops=0, retrans=0, no errors).

The instrumentation and what it caught

At this point, I asked the LLM to patch libfabric 2.5.1 to add instrumentation. After several attempts and disproven hypotheses, the model settled on logging the shm connreq handshake to /tmp/smr_diag_<pid>.log. On a stuck run (head node 192.168.92.216), the receiver side logged 8 dropped connreqs, e.g.:

connreq: pid=6775 DROPPED connreq from peer name=fe80::...0005... tx_id=6  idx=7   map_err=-2
connreq: pid=6845 DROPPED connreq from peer name=fe80::...0001... tx_id=46 idx=47  map_err=-2

map_err=-2 is -ENOENT. The senders whose connreqs were dropped logged a matching permanent stall:

verify_peer: pid=6776 peer=6  STUCK 1M attempts: name_sent=1 peer_id=-1 region_mapped=1
verify_peer: pid=6846 peer=46 STUCK 1M attempts: name_sent=1 peer_id=-1 region_mapped=1

name_sent=1 (the connreq was sent), peer_id=-1 (no acknowledgement ever came back), region_mapped=1 (the peer's shared-memory region did become mappable later, after the failure). There were zero "command queue full" events, so this is a mapping failure, not queue exhaustion (this was a previous hypothesis that the model had).

The code-level root cause the model identified (I can't personally verify this)

⚠️ This section is the model's reading of the code and goes beyond my own understanding of libfabric. I'm including it in case it's a useful starting point, but please treat it as a hypothesis to check rather than a claim I can stand behind. ⚠️

For each local rank pair, shm establishes a connection once via a connreq. Line references are to libfabric 2.5.1, unchanged on current main.

  1. Sender: smr_send_name() (prov/shm/src/smr_ep.c:172) puts a connreq into the peer's command queue and sets name_sent = 1 (line 196), so it will never send another connreq to that peer (guarded at line 181).
  2. Receiver:smr_progress_connreq() (prov/shm/src/smr_progress.c:827) consumes the connreq and calls smr_map_to_region() to map the sender's /dev/shm region (line 838). If that returns -ENOENT (the sender's shm file isn't visible yet), it does goto out (line 842) and skips writing the acknowledgement back into the sender's region (the smr_peer_data(peer_smr)[cmd->hdr.tx_id].id = idx at line 869 is never reached). The command-queue slot is then released regardless (smr_progress_cmd(), line 1348), so the connreq is gone.
  3. Sender: spins in smr_verify_peer() (prov/shm/src/smr_ep.c:200) returning EAGAIN forever: its peer id was never set (early-out at line 210 never taken) and name_sent=1 blocks any retry (line 181).

This matches the logs: the receiver's map_err=-2 drop and the sender's name_sent=1 peer_id=-1 stall are the two ends of the same broken handshake.

Why it's intermittent, not always fatal (again, the model's reasoning): if the peer later sends to the stuck sender, it issues a connreq in the reverse direction; processing that reverse connreq reaches line 860 (smr_peer_data(ep->region)[idx].id = cmd->hdr.tx_id) and completes the id write on the originally-stuck side, unsticking it. So a dropped connreq would only strand a link permanently when traffic on that edge is effectively one-directional. This is at least consistent with only some runs hanging, and only some ranks within them.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions