Skip to content

prov/shm, prov/efa: Allow more than 256 shm peers - #13180

Open
jiaxiyan wants to merge 4 commits into
ofiwg:mainfrom
jiaxiyan:shm-max-peers-1024
Open

jiaxiyan wants to merge 4 commits into
ofiwg:mainfrom
jiaxiyan:shm-max-peers-1024

Conversation

@jiaxiyan

@jiaxiyan jiaxiyan commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

shm supports at most 256 peers per AV, fixed at compile time. On instances with high core counts, a job with more than 256 processes per host fails: the 257th local peer fails fi_av_insert, and MPI aborts in MPI_Init:

efa_rdm_av_entry_insert_shm_av(): Max number of shm AV entry (256) has been reached.
efa_rdm_av_entry_alloc_explicit(): Failed to insert fi_addr 256 into shm provider's AV: Cannot allocate memory

This series keeps 256 as the default and lets the limit be raised at runtime, without changing memory use at the default.

  • prov/shm: Size the SAR pool independently of SMR_MAX_PEERS. The SAR pool stays at 256 buffers regardless of the peer limit, and the per-peer SAR quota is clamped to at least 1 so it can't drop to 0 when peers outnumber SAR buffers.
  • prov/shm: Let the AV count raise the peer limit. An AV opened with fi_av_attr.count above 256 raises its limit to that count, up to 4096. Smaller counts keep the default. The peer map and each endpoint's peer data are sized from the limit.
  • prov/efa: Allow raising FI_EFA_SHM_AV_SIZE above 256. FI_EFA_SHM_AV_SIZE (default 256) can now be set up to 4096.

@jiaxiyan
jiaxiyan requested review from a team, j-xiong and zachdworkin November 9, 2026 20:39
Comment thread prov/shm/src/smr_av.c Resolved
Comment thread prov/shm/src/smr.h Resolved
a-szegel
a-szegel previously approved these changes Nov 10, 2026
Comment thread prov/shm/src/smr_av.c
av_entry);
smr_ep = container_of(util_ep, struct smr_ep, util_ep);
smr_ep->region->max_sar_buf_per_peer =
MIN(SMR_BUF_BATCH_MAX,

@shijin-aws shijin-aws Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we convinced that the SAR pool size not changed when num peers goes up will NOT cause performance issue? I think Intel treats SAR as a fast path which works with DSA

j-xiong
j-xiong previously approved these changes Nov 10, 2026

@zachdworkin zachdworkin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! The SAR pool decoupling looks right to me but I have a
concern with the overall approach.

Tying capacity to fi_av_attr.count makes a hint into a hard limit, which is not how other providers treat it. It also means each process can end up with a different max_peers, so it has to be threaded through smr_attr and smr_calculate_size_offsets.

My suggestion: use one node-wide capacity instead of deriving it from the AV. It
could be a constant, or computed once at init, e.g. max(256, SMR_PROCS_PER_CORE * online_cores). (Default SMR_PROCS_PER_CORE to 1)
All processes on the node then lay out regions identically and the AV count stays a hint. Store max_peers it in the region header and we will have to bump SMR_VERSION so a mismatch is detectable. If we are bumping the version we should try to merge this change with the other one to avoid bumping the version too many times. (I am working on a new fallback protocol for #13084 and will open a PR soon).

To keep the cost proportional to peers actually used we could also make all-zero
mean free for smr_peer_data. The region is sized with ftruncate, so
untouched pages aren't committed, but the init loop in smr_create writes every
row. Dropping it would let a region pay only for the rows it uses. (I havent measured the performance for doing this)

Regarding the SAR quota issue Shi raised: the batch size is 256 / AV size, set by how many peers are in the AV, not by how many are active. I measured it with CMA off and a single active sender: bandwidth is flat up to 16 AV entries, then drops about 36-44% at 256 entries (batch of 1). A CMA-on control stays flat, so it is the SAR batch, not the AV, that costs the bandwidth. This already exists for AVs of 256 or fewer, but this change extends it.

Another issue with this change is the resources that the increased number of peers will compete for stays the same. None of the other pools are dynamically changing based on number of peers. They are all based on tx and rx sizes at init/create time. Senders may get FI_EAGAIN errors more often when trying to send due to resource constraints because of this. If you need the functionality today we can keep this change while working towards a more broad architectural solution to address this.

Closing notes: There is no provider-independent fabtest for shm AV capacity. av_test is in shm.exclude, and the other shm tests use two processes, so the new behavior would be untested. Could you add a test, or enable the relevant av_test cases for shm, that inserts more than 256 addresses and checks the limit? The EFA gtests don't cover shm itself.

Comment thread prov/shm/src/smr_av.c Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You missed this one. smr_av->used < smr_av->smr_map.max_peers

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing it! I have updated it to use your suggested approach and also added a new fabtest

Comment thread prov/shm/src/smr_av.c Resolved
@zachdworkin

Copy link
Copy Markdown
Contributor

May fix #11671 (partially)

shijin-aws
shijin-aws previously approved these changes Nov 10, 2026
Comment thread prov/shm/src/smr_util.h Outdated

int pid;
int resv2;
int max_peers;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need the entire int to be max_peers. Do we want to make uint16_t max_peers and keep the rest of the int as resv2. We have to be careful here b/c int is not a fixed size type.

Comment thread prov/shm/src/smr_util.c Resolved
The SAR pool size was tied to SMR_MAX_PEERS, so raising the peer limit
would grow every region by 32 KiB per peer. Give the pool its own size.

The per-peer SAR quota becomes 0 once peers outnumber SAR buffers,
which blocks SAR for every peer. Clamp it to at least 1, and don't
start a SAR transfer when the pool is empty.

Signed-off-by: Jessie Yang <jiaxiyan@amazon.com>
shm is limited to 256 peers per AV, which is not enough for instances
with high core counts. Compute one node-wide limit at init,
max(256, SMR_PROCS_PER_CORE * online cores), so every process on the
node lays out its region the same way and the AV count stays a hint.

Store the limit in the region header and bump SMR_VERSION so peers with
a different layout are rejected. Also reset shm_id on every iteration of
smr_av_insert so a failed insert does not remove the previous peer.

Signed-off-by: Jessie Yang <jiaxiyan@amazon.com>
FI_EFA_SHM_AV_SIZE defaulted to 256, so jobs on instances with high
core counts failed at the 257th local peer. Default it to the number of
online CPUs, with a minimum of 256, to match shm's peer limit. Setting
the variable still overrides the default.

shm now rejects AV counts above its limit, so drop EFA's own maximum
and warn when the shm AV cannot be opened. Add gtests for the default,
the limit, and an AV open above shm's limit.

The cmocka harness reset efa_env to a copy taken before provider init,
which no longer holds the shm AV size. Take the copy after init.

Signed-off-by: Jessie Yang <jiaxiyan@amazon.com>

@zachdworkin zachdworkin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding the fixes I asked for. Most of my additional comments are about the new test. I am still concerned about the point Shi brought up about the SAR batch size per peer. I think it is okay for now to get the functionality working for you guys, but like I said in my previous review a bigger re-architecture for how that works with more peers is going to be required for completeness of this feature. If you want to work together on that in the future please let me know!

Comment thread fabtests/unit/av_capacity_test.c Outdated
return ret;
}

static int parse_count(const char *arg, int *count)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this really necessary? Can you use something like atoi?

Comment thread fabtests/unit/av_capacity_test.c Outdated
if (len < 0 || len >= (int) sizeof(addr))
return -FI_ETOOSMALL;

*fi_addr = FI_ADDR_NOTAVAIL;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should probably set it to something else here because this is what the provider is expected to set the fi_addr to if it fails. This way we can distinguish if the test set it to something vs the provider setting it to something

Comment thread fabtests/unit/av_capacity_test.c Outdated
goto close;
}

if (ret > 0 || fi_addrs[inserted] != FI_ADDR_NOTAVAIL) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this check? I can't really find a case where we get to here without having catastrophically failed earlier (like inserted 2 addresses on the last iteration of the for loop incurring a segfault) or having skipped it and gone to close on line 90.

Comment thread fabtests/unit/av_capacity_test.c Outdated

if (inserted == max_addrs) {
ret = 0;
goto close;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this goto close is making it so the additional remove and insert one logic always gets skipped. Is that intended?

Comment thread fabtests/unit/av_capacity_test.c Outdated
{
ft_unit_usage(name, "Unit test for AV capacity");
FT_PRINT_OPTS_USAGE("-n <num>",
"minimum number of addresses the AV must accept (default 256)");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the default minimum 256?

Insert addresses into an AV until it is full, check that it accepts at
least a given number, rejects the next insert, and accepts an insert
again after a remove. Run it for shm with the minimum set to
max(256, online CPUs).

Signed-off-by: Jessie Yang <jiaxiyan@amazon.com>

This branch has not been deployed

No deployments
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.

5 participants