2424#include < string>
2525#include < sys/syscall.h>
2626#include < sys/time.h>
27+ #include < time.h>
2728#include < unistd.h>
2829using namespace std ;
2930
@@ -1402,6 +1403,8 @@ void *flagcxProxyService(void *args) {
14021403// Bypasses RMA Proxy: posts IB ops directly from kernel proxy thread.
14031404// ============================================================================
14041405
1406+ // Note: each kernel proxy thread allocates nRanks × (256 × 16B + 8B) ≈
1407+ // 4KB/peer. With contextCount threads, total is contextCount × nRanks × 4KB.
14051408#define FLAGCX_KPROXY_MAX_INFLIGHT 256
14061409#define FLAGCX_KPROXY_RING_MASK (FLAGCX_KPROXY_MAX_INFLIGHT - 1 )
14071410
@@ -1426,7 +1429,9 @@ static void flagcxKernelProxyPoll(struct flagcxKernelProxyState *state,
14261429 struct flagcxHeteroComm *comm) {
14271430 struct flagcxRmaProxyState *proxy = comm->rmaProxy ;
14281431 struct flagcxNetAdaptor *net = comm->netAdaptor ;
1429- bool anyCompleted = false ;
1432+ if (proxy == NULL || net == NULL )
1433+ return ;
1434+ bool anyRetired = false ;
14301435 for (int p = 0 ; p < state->nRanks ; p++) {
14311436 struct flagcxKernelProxyPeerState *ps = &state->peers [p];
14321437 while (ps->head != ps->tail ) {
@@ -1451,6 +1456,7 @@ static void flagcxKernelProxyPoll(struct flagcxKernelProxyState *state,
14511456 if (!done)
14521457 break ;
14531458 ps->head ++;
1459+ anyRetired = true ;
14541460 // Always advance doneSeqs (even on failure) to prevent flush hangs.
14551461 __atomic_store_n ((uint64_t *)&proxy->doneSeqs [p], inf->opSeq ,
14561462 __ATOMIC_RELEASE);
@@ -1459,13 +1465,11 @@ static void flagcxKernelProxyPoll(struct flagcxKernelProxyState *state,
14591465 __ATOMIC_RELEASE);
14601466 if (!failed) {
14611467 __atomic_fetch_add (&proxy->completionCount , 1ULL , __ATOMIC_RELEASE);
1462- anyCompleted = true ;
14631468 }
14641469 }
14651470 }
1466- // Batch the broadcast: signal waiters once per poll sweep, not per
1467- // completion.
1468- if (anyCompleted && !proxy->useStreamOps ) {
1471+ // Batch the broadcast: signal waiters once per poll sweep on any retirement.
1472+ if (anyRetired && !proxy->useStreamOps ) {
14691473 pthread_mutex_lock (&proxy->doneMutex );
14701474 pthread_cond_broadcast (&proxy->doneCond );
14711475 pthread_mutex_unlock (&proxy->doneMutex );
@@ -1504,12 +1508,18 @@ flagcxKernelProxyPost(struct flagcxKernelProxyState *state,
15041508 }
15051509
15061510 // Back-pressure: if ring is full, poll until a slot frees.
1507- int spins = 0 ;
1511+ struct timespec deadline;
1512+ clock_gettime (CLOCK_MONOTONIC , &deadline);
1513+ deadline.tv_sec += 30 ; // 30s timeout
15081514 while ((ps->tail - ps->head ) >= FLAGCX_KPROXY_MAX_INFLIGHT ) {
15091515 flagcxKernelProxyPoll (state, comm);
15101516 if ((ps->tail - ps->head ) >= FLAGCX_KPROXY_MAX_INFLIGHT ) {
1511- if (++spins > 10000000 ) {
1512- WARN (" flagcxKernelProxyPost: back-pressure timeout peer=%d" , peer);
1517+ struct timespec now;
1518+ clock_gettime (CLOCK_MONOTONIC , &now);
1519+ if (now.tv_sec > deadline.tv_sec ||
1520+ (now.tv_sec == deadline.tv_sec && now.tv_nsec >= deadline.tv_nsec )) {
1521+ WARN (" flagcxKernelProxyPost: back-pressure timeout (30s) peer=%d" ,
1522+ peer);
15131523 __atomic_store_n ((int *)&proxy->rmaError , 1 , __ATOMIC_RELEASE);
15141524 return flagcxInternalError;
15151525 }
@@ -1518,13 +1528,22 @@ flagcxKernelProxyPost(struct flagcxKernelProxyState *state,
15181528 }
15191529
15201530 void *sendComm = comm->oneSideHandles [0 ]->fullSendComms [peer];
1531+ if (sendComm == NULL ) {
1532+ WARN (" flagcxKernelProxyPost: sendComm is NULL for peer=%d" , peer);
1533+ return flagcxInternalError;
1534+ }
15211535 void **srcHandles = NULL , **dstHandles = NULL ;
1522- if (size > 0 && srcMrIdx >= 0 ) {
1536+ if (size > 0 && srcMrIdx >= 0 && dstMrIdx >= 0 ) {
15231537 srcHandles = (void **)comm->oneSideHandles [srcMrIdx];
15241538 dstHandles = (void **)comm->oneSideHandles [dstMrIdx];
15251539 }
15261540
1527- // Assign opSeq (shared atomic counter with RMA Proxy for ordering)
1541+ // Assign opSeq (shared atomic counter with RMA Proxy for ordering).
1542+ // Single-writer invariant: for a given peer, only the kernel proxy thread
1543+ // (this path) or the RMA Proxy thread (stream path) posts ops at any time.
1544+ // This ensures opSeqs are assigned and complete in order, so doneSeqs
1545+ // advances monotonically. Violating this invariant would require per-peer
1546+ // max-tracking instead of direct store.
15281547 uint64_t opSeq =
15291548 __atomic_add_fetch ((uint64_t *)&proxy->opSeqs [peer], 1 , __ATOMIC_RELAXED);
15301549
@@ -1550,6 +1569,28 @@ flagcxKernelProxyPost(struct flagcxKernelProxyState *state,
15501569 break ;
15511570 }
15521571 case FLAGCX_RMA_PUT_VALUE : {
1572+ // Serialize PUT_VALUE per-peer: drain all prior ops to ensure the staging
1573+ // slot is idle. The NIC reads staging asynchronously, so concurrent
1574+ // PUT_VALUE to the same peer would corrupt the buffer.
1575+ struct timespec pvDeadline;
1576+ clock_gettime (CLOCK_MONOTONIC , &pvDeadline);
1577+ pvDeadline.tv_sec += 30 ;
1578+ while (ps->head != ps->tail ) {
1579+ flagcxKernelProxyPoll (state, comm);
1580+ if (ps->head != ps->tail ) {
1581+ struct timespec now;
1582+ clock_gettime (CLOCK_MONOTONIC , &now);
1583+ if (now.tv_sec > pvDeadline.tv_sec ||
1584+ (now.tv_sec == pvDeadline.tv_sec &&
1585+ now.tv_nsec >= pvDeadline.tv_nsec )) {
1586+ WARN (" flagcxKernelProxyPost: PUT_VALUE drain timeout (30s) peer=%d" ,
1587+ peer);
1588+ __atomic_store_n ((int *)&proxy->rmaError , 1 , __ATOMIC_RELEASE);
1589+ return flagcxInternalError;
1590+ }
1591+ sched_yield ();
1592+ }
1593+ }
15531594 struct flagcxOneSideHandleInfo *stagingH = comm->stagingHandle ;
15541595 if (stagingH == NULL || stagingH->baseVas == NULL ) {
15551596 WARN (" flagcxKernelProxyPost: staging handles not initialized" );
@@ -1593,6 +1634,8 @@ static void flagcxKernelProxyDrain(struct flagcxKernelProxyState *state,
15931634 return ;
15941635 struct flagcxNetAdaptor *net = comm->netAdaptor ;
15951636 struct flagcxRmaProxyState *proxy = comm->rmaProxy ;
1637+ if (net == NULL || proxy == NULL )
1638+ return ;
15961639 for (int p = 0 ; p < state->nRanks ; p++) {
15971640 struct flagcxKernelProxyPeerState *ps = &state->peers [p];
15981641 while (ps->head != ps->tail ) {
@@ -1609,6 +1652,8 @@ static void flagcxKernelProxyDrain(struct flagcxKernelProxyState *state,
16091652 done = 1 ;
16101653 break ;
16111654 }
1655+ if (!done)
1656+ sched_yield ();
16121657 }
16131658 succeeded = !testFailed;
16141659 }
0 commit comments