Skip to content

Commit 5baa6cb

Browse files
committed
fix(p2p): use pthread_mutex for MR lifecycle serialization
1 parent 50a940b commit 5baa6cb

4 files changed

Lines changed: 79 additions & 16 deletions

File tree

flagcx/core/flagcx_mr_registry.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@ static void freeEntryExtensions(struct flagcxMrEntry *entry) {
174174
*/
175175
static flagcxResult_t idIndexAppend(struct flagcxMrRegistry *reg, uint64_t mrId,
176176
uintptr_t baseAddr) {
177+
if (reg->idCount > 0 && mrId <= reg->idIndex[reg->idCount - 1].mrId) {
178+
WARN("flagcxMrRegistry: non-monotonic mrId %lu (last %lu)",
179+
(unsigned long)mrId,
180+
(unsigned long)reg->idIndex[reg->idCount - 1].mrId);
181+
return flagcxInternalError;
182+
}
177183
if (reg->idCount >= reg->idCapacity) {
178184
int newCap =
179185
reg->idCapacity == 0 ? ID_INDEX_INITIAL_CAPACITY : reg->idCapacity * 2;
@@ -373,6 +379,8 @@ flagcxResult_t flagcxMrRegistryRegister(struct flagcxMrRegistry *reg,
373379
if (existing->p2p) {
374380
if (existing->p2p->mrId == 0)
375381
existing->p2p->mrId = reg->nextId++;
382+
if (existing->p2p->mrId >= reg->nextId)
383+
reg->nextId = existing->p2p->mrId + 1;
376384
if (idIndexAppend(reg, existing->p2p->mrId, addr) != flagcxSuccess) {
377385
/* Roll back: remove P2P ownership, caller retains ext */
378386
existing->p2p = NULL;
@@ -469,6 +477,8 @@ flagcxResult_t flagcxMrRegistryRegister(struct flagcxMrRegistry *reg,
469477
/* Assign mrId from registry's monotonic counter if not pre-set */
470478
if (entry->p2p->mrId == 0)
471479
entry->p2p->mrId = reg->nextId++;
480+
if (entry->p2p->mrId >= reg->nextId)
481+
reg->nextId = entry->p2p->mrId + 1;
472482
if (idIndexAppend(reg, entry->p2p->mrId, addr) != flagcxSuccess) {
473483
/* Roll back: remove the entry we just inserted */
474484
entry->p2p = NULL; /* caller retains ext ownership */

flagcx/core/flagcx_p2p.cc

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ static uint64_t &nextXferId() {
372372
#define gXferMutex xferMutex()
373373
#define gNextXferId nextXferId()
374374

375+
static pthread_mutex_t gMrLifecycleMutex = PTHREAD_MUTEX_INITIALIZER;
376+
375377
struct FlagcxSliceCache {
376378
static constexpr size_t kCap = 4096;
377379
std::vector<FlagcxSlice *> ring;
@@ -2098,15 +2100,17 @@ void flagcxP2pEngineDestroy(FlagcxP2pEngine *engine) {
20982100
}
20992101

21002102
{
2101-
/* Phase 1: collect P2P mhandle info under write lock */
2103+
pthread_mutex_lock(&gMrLifecycleMutex);
2104+
2105+
/* Phase 1: collect P2P mhandle info under read lock */
21022106
struct P2pDeregInfo {
21032107
int ibDevN;
21042108
void *mhandle;
21052109
uintptr_t baseAddr;
21062110
};
21072111
std::vector<P2pDeregInfo> deregList;
21082112

2109-
flagcxMrRegistryWrLock(flagcxGlobalMrRegistry);
2113+
flagcxMrRegistryRdLock(flagcxGlobalMrRegistry);
21102114
int count = flagcxMrRegistryCount(flagcxGlobalMrRegistry);
21112115
struct flagcxMrEntry *entries =
21122116
flagcxMrRegistryEntries(flagcxGlobalMrRegistry);
@@ -2119,7 +2123,7 @@ void flagcxP2pEngineDestroy(FlagcxP2pEngine *engine) {
21192123
info.baseAddr = entries[i].baseAddr;
21202124
deregList.push_back(info);
21212125
}
2122-
flagcxMrRegistryWrUnlock(flagcxGlobalMrRegistry);
2126+
flagcxMrRegistryRdUnlock(flagcxGlobalMrRegistry);
21232127

21242128
/* Phase 2: deregister from registry first (atomically removes from lookup)
21252129
*/
@@ -2135,6 +2139,8 @@ void flagcxP2pEngineDestroy(FlagcxP2pEngine *engine) {
21352139
} devCtx = {info.ibDevN};
21362140
engine->adaptor->deregMr(&devCtx, info.mhandle);
21372141
}
2142+
2143+
pthread_mutex_unlock(&gMrLifecycleMutex);
21382144
}
21392145

21402146
/* Release P2P engine's refcount on the global MR registry */
@@ -2477,6 +2483,8 @@ int flagcxP2pEngineReg(FlagcxP2pEngine *engine, uintptr_t data, size_t size,
24772483
if (engine == NULL || data == 0)
24782484
return -1;
24792485

2486+
pthread_mutex_lock(&gMrLifecycleMutex);
2487+
24802488
/* Check for existing exact-match registration (dedup) */
24812489
{
24822490
struct flagcxMrEntry existing;
@@ -2490,9 +2498,11 @@ int flagcxP2pEngineReg(FlagcxP2pEngine *engine, uintptr_t data, size_t size,
24902498
WARN("P2P Reg: addr 0x%lx size mismatch: existing %zu vs requested "
24912499
"%zu",
24922500
(unsigned long)data, existing.size, size);
2501+
pthread_mutex_unlock(&gMrLifecycleMutex);
24932502
return -1;
24942503
}
24952504
mrId = p2pExt.p2p.mrId;
2505+
pthread_mutex_unlock(&gMrLifecycleMutex);
24962506
return 0;
24972507
}
24982508
}
@@ -2520,6 +2530,7 @@ int flagcxP2pEngineReg(FlagcxP2pEngine *engine, uintptr_t data, size_t size,
25202530
ptrType, FLAGCX_NET_MR_FLAG_NONE,
25212531
&mhandle) != flagcxSuccess ||
25222532
mhandle == NULL) {
2533+
pthread_mutex_unlock(&gMrLifecycleMutex);
25232534
return -1;
25242535
}
25252536

@@ -2528,6 +2539,7 @@ int flagcxP2pEngineReg(FlagcxP2pEngine *engine, uintptr_t data, size_t size,
25282539
(struct flagcxMrP2pExt *)calloc(1, sizeof(struct flagcxMrP2pExt));
25292540
if (p2pExt == NULL) {
25302541
engine->adaptor->deregMr(&devCtx, mhandle);
2542+
pthread_mutex_unlock(&gMrLifecycleMutex);
25312543
return -1;
25322544
}
25332545
/* mrId=0 signals registry to assign from its monotonic counter */
@@ -2544,34 +2556,48 @@ int flagcxP2pEngineReg(FlagcxP2pEngine *engine, uintptr_t data, size_t size,
25442556
if (res != flagcxSuccess) {
25452557
engine->adaptor->deregMr(&devCtx, mhandle);
25462558
free(p2pExt);
2559+
pthread_mutex_unlock(&gMrLifecycleMutex);
25472560
return -1;
25482561
}
25492562

25502563
mrId = assignedId;
2564+
pthread_mutex_unlock(&gMrLifecycleMutex);
25512565
return 0;
25522566
}
25532567

25542568
void flagcxP2pEngineMrDestroy(FlagcxP2pEngine *engine, FlagcxP2pMr mr) {
25552569
if (engine == NULL)
25562570
return;
25572571

2572+
pthread_mutex_lock(&gMrLifecycleMutex);
2573+
25582574
/* Find entry by mrId to get baseAddr for deregister */
25592575
struct flagcxMrEntry mrEntry;
25602576
if (flagcxMrRegistryLookupById(flagcxGlobalMrRegistry, mr, &mrEntry, NULL) !=
2561-
flagcxSuccess)
2577+
flagcxSuccess) {
2578+
pthread_mutex_unlock(&gMrLifecycleMutex);
25622579
return;
2580+
}
25632581

25642582
/* Remove from registry first — prevents concurrent readers from finding it */
25652583
void *removedExt = NULL;
2566-
flagcxMrRegistryDeregister(flagcxGlobalMrRegistry, mrEntry.baseAddr,
2567-
FLAGCX_MR_OWNER_P2P, NULL, &removedExt);
2568-
free(removedExt);
2569-
2570-
/* Now safe to deregister the adaptor handle */
2584+
flagcxResult_t res;
25712585
struct {
25722586
int ibDevN;
25732587
} devCtx = {mrEntry.ibDevN};
2588+
FLAGCXCHECKGOTO(
2589+
flagcxMrRegistryDeregister(flagcxGlobalMrRegistry, mrEntry.baseAddr,
2590+
FLAGCX_MR_OWNER_P2P, NULL, &removedExt),
2591+
res, fail);
2592+
free(removedExt);
2593+
2594+
/* Now safe to deregister the adaptor handle */
25742595
engine->adaptor->deregMr(&devCtx, mrEntry.mhandles[FLAGCX_MR_OWNER_IDX_P2P]);
2596+
pthread_mutex_unlock(&gMrLifecycleMutex);
2597+
return;
2598+
2599+
fail:
2600+
pthread_mutex_unlock(&gMrLifecycleMutex);
25752601
}
25762602

25772603
int flagcxP2pEnginePrepareDesc(FlagcxP2pEngine *engine, FlagcxP2pMr mr,
@@ -2616,6 +2642,23 @@ int flagcxP2pEnginePrepareDesc(FlagcxP2pEngine *engine, FlagcxP2pMr mr,
26162642
return -1;
26172643
}
26182644

2645+
/* Verify (data, size) falls within registered MR region (overflow-safe) */
2646+
uintptr_t dataAddr = (uintptr_t)data;
2647+
if (dataAddr < entries[idx].baseAddr) {
2648+
flagcxMrRegistryWrUnlock(flagcxGlobalMrRegistry);
2649+
return -1;
2650+
}
2651+
size_t offset = (size_t)(dataAddr - entries[idx].baseAddr);
2652+
if (offset > entries[idx].size || size > entries[idx].size - offset) {
2653+
flagcxMrRegistryWrUnlock(flagcxGlobalMrRegistry);
2654+
return -1;
2655+
}
2656+
2657+
if (size > UINT32_MAX) {
2658+
flagcxMrRegistryWrUnlock(flagcxGlobalMrRegistry);
2659+
return -1;
2660+
}
2661+
26192662
FlagcxP2pRdmaDesc desc;
26202663
memset(&desc, 0, sizeof(desc));
26212664
desc.addr = (uint64_t)(uintptr_t)data;

flagcx/core/include/flagcx_mr_registry.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ struct flagcxMrRegistry {
105105
int capacity;
106106
uint64_t nextId; /* monotonic ID generator for all subsystems */
107107

108-
struct flagcxMrIdEntry *idIndex; /* sorted by mrId (ascending, append-only) */
108+
struct flagcxMrIdEntry *idIndex; /* sorted by mrId (ascending) */
109109
int idCount;
110110
int idCapacity;
111111

@@ -130,9 +130,10 @@ flagcxResult_t flagcxMrRegistryDestroy(struct flagcxMrRegistry *reg);
130130
* ownerBit: one of FLAGCX_MR_OWNER_{P2P,COLL,RMA}
131131
* mhandle: adaptor handle (stored in mhandles[ownerIdx])
132132
* ext: subsystem extension struct pointer (ownership transferred to entry)
133-
* outId: if non-NULL, returns a monotonic ID. For P2P, this is persisted
134-
* in p2p->mrId and stable across repeated calls. For COLL/RMA,
135-
* this is a one-shot assignment (not stored on the entry).
133+
* outId: if non-NULL, receives a monotonic ID on first registration.
134+
* For P2P: persisted in p2p->mrId, stable across repeated calls.
135+
* For COLL/RMA: one-shot assignment (not stored on entry);
136+
* re-registration of an already-owned entry sets *outId = 0.
136137
*/
137138
flagcxResult_t flagcxMrRegistryRegister(struct flagcxMrRegistry *reg,
138139
uintptr_t addr, size_t size, int ibDevN,
@@ -218,6 +219,16 @@ struct flagcxMrEntry *flagcxMrRegistryEntries(struct flagcxMrRegistry *reg);
218219
extern struct flagcxMrRegistry *flagcxGlobalMrRegistry;
219220

220221
flagcxResult_t flagcxMrRegistryGlobalInit(void);
222+
223+
/*
224+
* Release one reference to the global registry. Destroys when refcount
225+
* reaches 0.
226+
*
227+
* Precondition: all data-path operations (Lookup, LookupById, PrepareDesc)
228+
* must be quiesced before the final release. The caller is responsible for
229+
* ensuring no concurrent registry access is in flight when the last
230+
* reference is dropped. Violating this is undefined behavior (use-after-free).
231+
*/
221232
flagcxResult_t flagcxMrRegistryGlobalRelease(void);
222233

223234
#ifdef __cplusplus

test/unittest/core/test_mr_registry.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -525,9 +525,8 @@ TEST_F(MrRegistryTest, ConcurrentWriters) {
525525
std::atomic<int> errors{0};
526526
auto worker = [&](int tid) {
527527
for (int i = 0; i < kOpsPerThread; i++) {
528-
// Each thread uses a unique address range: tid * large_gap + i * page
529-
uintptr_t addr =
530-
(uintptr_t)(tid + 1) * 0x100000000ULL + (uintptr_t)i * kPageSize;
528+
// Each thread uses a unique address range: tid * (1<<20) + i * page
529+
uintptr_t addr = ((uintptr_t)(tid + 1) << 20) + (uintptr_t)i * kPageSize;
531530
size_t size = kPageSize;
532531

533532
struct flagcxMrP2pExt *ext =

0 commit comments

Comments
 (0)