Skip to content

Commit 5393148

Browse files
committed
Add interface func and test
1 parent 42d5dd5 commit 5393148

5 files changed

Lines changed: 240 additions & 14 deletions

File tree

flagcx/adaptor/device/kunlunxin_adaptor.cc

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ flagcxResult_t kunlunAdaptorGetVendor(char *vendor) {
103103
}
104104

105105
flagcxResult_t kunlunAdaptorHostGetDevicePointer(void **pDevice, void *pHost) {
106+
if (pDevice == NULL || pHost == NULL) {
107+
return flagcxInvalidArgument;
108+
}
106109
DEVCHECK(cudaHostGetDevicePointer(pDevice, pHost, 0));
107110
return flagcxSuccess;
108111
}
@@ -331,16 +334,29 @@ flagcxResult_t kunlunAdaptorGetDeviceProperties(struct flagcxDevProps *props,
331334
return flagcxInvalidArgument;
332335
}
333336

337+
// Get device name via cudaGetDeviceProperties
334338
cudaDeviceProp devProp;
335339
DEVCHECK(cudaGetDeviceProperties(&devProp, dev));
336340
strncpy(props->name, devProp.name, sizeof(props->name) - 1);
337341
props->name[sizeof(props->name) - 1] = '\0';
338-
props->pciBusId = devProp.pciBusID;
339-
props->pciDeviceId = devProp.pciDeviceID;
340-
props->pciDomainId = devProp.pciDomainID;
341-
// TODO: see if there's another way to get this info. In some cuda versions,
342-
// cudaDeviceProp does not have `gpuDirectRDMASupported` field
343-
// props->gdrSupported = devProp.gpuDirectRDMASupported;
342+
343+
// XPU runtime does not write PCI fields in cudaDeviceProp.
344+
// Parse them from the stable PCI bus ID string instead.
345+
char pciBusIdStr[FLAGCX_DEVICE_PCI_BUSID_BUFFER_SIZE] = {};
346+
DEVCHECK(cudaDeviceGetPCIBusId(pciBusIdStr, sizeof(pciBusIdStr), dev));
347+
348+
// Format: "DDDD:BB:SS.F"
349+
unsigned int domain = 0, bus = 0, slot = 0, func = 0;
350+
if (sscanf(pciBusIdStr, "%x:%x:%x.%x", &domain, &bus, &slot, &func) != 4) {
351+
return flagcxInternalError;
352+
}
353+
if (domain > 0xffff || bus > 0xff || slot > 0x1f || func > 0x7) {
354+
return flagcxInternalError;
355+
}
356+
357+
props->pciDomainId = static_cast<int>(domain);
358+
props->pciBusId = static_cast<int>(bus);
359+
props->pciDeviceId = static_cast<int>(slot);
344360

345361
return flagcxSuccess;
346362
}
@@ -371,16 +387,30 @@ flagcxResult_t kunlunAdaptorStreamWriteValue64(flagcxStream_t, void *, uint64_t,
371387
int) {
372388
return flagcxNotSupported;
373389
}
374-
flagcxResult_t kunlunAdaptorEventElapsedTime(float *, flagcxEvent_t,
375-
flagcxEvent_t) {
376-
return flagcxNotSupported;
390+
flagcxResult_t kunlunAdaptorEventElapsedTime(float *ms, flagcxEvent_t start,
391+
flagcxEvent_t end) {
392+
if (ms == NULL || start == NULL || end == NULL) {
393+
return flagcxInvalidArgument;
394+
}
395+
DEVCHECK(cudaEventElapsedTime(ms, start->base, end->base));
396+
return flagcxSuccess;
377397
}
378398

379-
flagcxResult_t kunlunAdaptorHostRegister(void *, size_t) {
380-
return flagcxNotSupported;
399+
flagcxResult_t kunlunAdaptorHostRegister(void *ptr, size_t size) {
400+
if (ptr == NULL || size == 0) {
401+
return flagcxInvalidArgument;
402+
}
403+
// XPU's cudaHostRegisterMapped triggers a runtime assertion crash
404+
// (rm_mem.cc:2284), so use Default flag to register as page-locked memory.
405+
DEVCHECK(cudaHostRegister(ptr, size, cudaHostRegisterDefault));
406+
return flagcxSuccess;
381407
}
382-
flagcxResult_t kunlunAdaptorHostUnregister(void *) {
383-
return flagcxNotSupported;
408+
flagcxResult_t kunlunAdaptorHostUnregister(void *ptr) {
409+
if (ptr == NULL) {
410+
return flagcxInvalidArgument;
411+
}
412+
DEVCHECK(cudaHostUnregister(ptr));
413+
return flagcxSuccess;
384414
}
385415

386416
// Symmetric memory VMM stubs (not supported)

flagcx/flagcx.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ static struct flagcxDeviceHandle globalDeviceHandle {
8484
// Event functions
8585
deviceAdaptor->eventCreate, deviceAdaptor->eventDestroy,
8686
deviceAdaptor->eventRecord, deviceAdaptor->eventSynchronize,
87-
deviceAdaptor->eventQuery,
87+
deviceAdaptor->eventQuery, deviceAdaptor->eventElapsedTime,
8888
// IpcMemHandle functions
8989
deviceAdaptor->ipcMemHandleCreate, deviceAdaptor->ipcMemHandleGet,
9090
deviceAdaptor->ipcMemHandleOpen, deviceAdaptor->ipcMemHandleClose,
@@ -117,6 +117,7 @@ void flagcxRebuildGlobalDeviceHandle() {
117117
globalDeviceHandle.eventRecord = deviceAdaptor->eventRecord;
118118
globalDeviceHandle.eventSynchronize = deviceAdaptor->eventSynchronize;
119119
globalDeviceHandle.eventQuery = deviceAdaptor->eventQuery;
120+
globalDeviceHandle.eventElapsedTime = deviceAdaptor->eventElapsedTime;
120121
// IpcMemHandle functions
121122
globalDeviceHandle.ipcMemHandleCreate = deviceAdaptor->ipcMemHandleCreate;
122123
globalDeviceHandle.ipcMemHandleGet = deviceAdaptor->ipcMemHandleGet;

flagcx/include/flagcx.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ struct flagcxDeviceHandle {
172172
flagcxResult_t (*eventRecord)(flagcxEvent_t event, flagcxStream_t stream);
173173
flagcxResult_t (*eventSynchronize)(flagcxEvent_t event);
174174
flagcxResult_t (*eventQuery)(flagcxEvent_t event);
175+
flagcxResult_t (*eventElapsedTime)(float *ms, flagcxEvent_t start,
176+
flagcxEvent_t end);
175177
// IpcMemHandle functions
176178
flagcxResult_t (*ipcMemHandleCreate)(flagcxIpcMemHandle_t *handle,
177179
size_t *size);

plugin/interservice/flagcx_wrapper.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ class flagcxUniqueId(ctypes.Structure):
9393
EVENT_RECORD_FUNCTYPE = ctypes.CFUNCTYPE(flagcxResult_t, flagcxEvent_t, flagcxStream_t)
9494
EVENT_SYNCHRONIZE_FUNCTYPE = ctypes.CFUNCTYPE(flagcxResult_t, flagcxEvent_t)
9595
EVENT_QUERY_FUNCTYPE = ctypes.CFUNCTYPE(flagcxResult_t, flagcxEvent_t)
96+
EVENT_ELAPSED_TIME_FUNCTYPE = ctypes.CFUNCTYPE(
97+
flagcxResult_t, ctypes.POINTER(ctypes.c_float), flagcxEvent_t, flagcxEvent_t)
9698

9799
IPC_MEM_HANDLE_CREATE_FUNCTYPE = ctypes.CFUNCTYPE(flagcxResult_t, ctypes.POINTER(flagcxIpcMemHandle_t), ctypes.POINTER(ctypes.c_size_t))
98100
IPC_MEM_HANDLE_GET_FUNCTYPE = ctypes.CFUNCTYPE(flagcxResult_t, flagcxIpcMemHandle_t, ctypes.c_void_p)
@@ -127,6 +129,7 @@ class flagcxDeviceHandle(ctypes.Structure):
127129
("eventRecord", EVENT_RECORD_FUNCTYPE),
128130
("eventSynchronize", EVENT_SYNCHRONIZE_FUNCTYPE),
129131
("eventQuery", EVENT_QUERY_FUNCTYPE),
132+
("eventElapsedTime", EVENT_ELAPSED_TIME_FUNCTYPE),
130133
# IpcMemHandle functions
131134
("ipcMemHandleCreate", IPC_MEM_HANDLE_CREATE_FUNCTYPE),
132135
("ipcMemHandleGet", IPC_MEM_HANDLE_GET_FUNCTYPE),

test/unittest/adaptor/test_device_adaptor.cpp

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,3 +446,193 @@ int main(int argc, char **argv) {
446446
::testing::InitGoogleTest(&argc, argv);
447447
return RUN_ALL_TESTS();
448448
}
449+
// Test: obtain the device-visible alias of mapped host memory.
450+
TEST_F(DeviceAdaptorTest, HostGetDevicePointer) {
451+
ASSERT_NE(devHandle->hostGetDevicePointer, nullptr);
452+
void *hostPtr = nullptr;
453+
ASSERT_EQ(devHandle->deviceMalloc(&hostPtr, TEST_SIZE, flagcxMemHost, nullptr),
454+
flagcxSuccess);
455+
ASSERT_NE(hostPtr, nullptr);
456+
void *devicePtr = nullptr;
457+
EXPECT_EQ(devHandle->hostGetDevicePointer(&devicePtr, hostPtr), flagcxSuccess);
458+
EXPECT_NE(devicePtr, nullptr);
459+
EXPECT_EQ(devHandle->hostGetDevicePointer(nullptr, hostPtr),
460+
flagcxInvalidArgument);
461+
EXPECT_EQ(devHandle->hostGetDevicePointer(&devicePtr, nullptr),
462+
flagcxInvalidArgument);
463+
EXPECT_EQ(devHandle->deviceFree(hostPtr, flagcxMemHost, nullptr),
464+
flagcxSuccess);
465+
}
466+
467+
// Test: Host memory register / get device pointer / unregister
468+
// flagcxDeviceHandle does not expose hostRegister/hostUnregister, so we
469+
// access them through the internal deviceAdaptor pointer (from adaptor.h).
470+
// Portable across all backends: skips if not implemented (NULL or NotSupported).
471+
TEST_F(DeviceAdaptorTest, HostRegisterUnregister) {
472+
if (!deviceAdaptor->hostRegister || !deviceAdaptor->hostUnregister) {
473+
GTEST_SKIP() << "hostRegister/hostUnregister not implemented";
474+
}
475+
476+
const size_t sz = 4096;
477+
void *hostPtr = malloc(sz);
478+
ASSERT_NE(hostPtr, nullptr);
479+
480+
// Register host memory as pinned (page-locked) memory.
481+
auto result = deviceAdaptor->hostRegister(hostPtr, sz);
482+
if (result == flagcxNotSupported) {
483+
free(hostPtr);
484+
GTEST_SKIP() << "hostRegister not supported on this backend";
485+
}
486+
EXPECT_EQ(result, flagcxSuccess);
487+
488+
if (result == flagcxSuccess) {
489+
// Unregister
490+
auto r2 = deviceAdaptor->hostUnregister(hostPtr);
491+
EXPECT_EQ(r2, flagcxSuccess);
492+
}
493+
free(hostPtr);
494+
495+
// Verify invalid arguments are rejected (only if backend has real impl)
496+
auto nullResult = deviceAdaptor->hostRegister(NULL, sz);
497+
if (nullResult != flagcxNotSupported) {
498+
EXPECT_EQ(nullResult, flagcxInvalidArgument);
499+
}
500+
auto zeroResult = deviceAdaptor->hostRegister(hostPtr, 0);
501+
if (zeroResult != flagcxNotSupported) {
502+
EXPECT_EQ(zeroResult, flagcxInvalidArgument);
503+
}
504+
auto unregNullResult = deviceAdaptor->hostUnregister(NULL);
505+
if (unregNullResult != flagcxNotSupported) {
506+
EXPECT_EQ(unregNullResult, flagcxInvalidArgument);
507+
}
508+
}
509+
510+
// Test: getDevicePciBusId — string format and invalid-arg guards
511+
TEST_F(DeviceAdaptorTest, GetDevicePciBusId) {
512+
int numDevices = 0;
513+
ASSERT_EQ(devHandle->getDeviceCount(&numDevices), flagcxSuccess);
514+
ASSERT_GT(numDevices, 0);
515+
516+
for (int dev = 0; dev < numDevices; dev++) {
517+
char pciBusId[FLAGCX_DEVICE_PCI_BUSID_BUFFER_SIZE] = {};
518+
ASSERT_EQ(deviceAdaptor->getDevicePciBusId(pciBusId, sizeof(pciBusId), dev),
519+
flagcxSuccess)
520+
<< "getDevicePciBusId failed for device " << dev;
521+
EXPECT_NE(pciBusId[0], '\0') << "empty PCI bus ID for device " << dev;
522+
523+
// Verify format: domain:bus:device.function (4 hex fields)
524+
unsigned int domain = 0, bus = 0, pciDevice = 0, function = 0;
525+
EXPECT_EQ(sscanf(pciBusId, "%x:%x:%x.%x", &domain, &bus, &pciDevice, &function),
526+
4)
527+
<< "unexpected PCI bus ID format: " << pciBusId;
528+
529+
std::cout << "Device " << dev << ": pci=" << pciBusId << std::endl;
530+
}
531+
532+
// Invalid-argument guards
533+
char buf[FLAGCX_DEVICE_PCI_BUSID_BUFFER_SIZE] = {};
534+
EXPECT_EQ(deviceAdaptor->getDevicePciBusId(nullptr, sizeof(buf), 0),
535+
flagcxInvalidArgument);
536+
537+
// Out-of-range device index
538+
EXPECT_NE(deviceAdaptor->getDevicePciBusId(buf, sizeof(buf), -1), flagcxSuccess);
539+
EXPECT_NE(deviceAdaptor->getDevicePciBusId(buf, sizeof(buf), numDevices),
540+
flagcxSuccess);
541+
}
542+
543+
// Test: getDeviceProperties — name and PCI fields, invalid-arg guards
544+
TEST_F(DeviceAdaptorTest, GetDeviceProperties) {
545+
int numDevices = 0;
546+
ASSERT_EQ(devHandle->getDeviceCount(&numDevices), flagcxSuccess);
547+
ASSERT_GT(numDevices, 0);
548+
549+
for (int dev = 0; dev < numDevices; dev++) {
550+
flagcxDevProps props = {};
551+
ASSERT_EQ(deviceAdaptor->getDeviceProperties(&props, dev), flagcxSuccess)
552+
<< "getDeviceProperties failed for device " << dev;
553+
EXPECT_NE(props.name[0], '\0') << "empty device name for device " << dev;
554+
555+
// PCI fields are parsed from cudaDeviceGetPCIBusId string
556+
EXPECT_GE(props.pciBusId, 0) << "pciBusId out of range for device " << dev;
557+
EXPECT_LE(props.pciBusId, 255) << "pciBusId out of range for device " << dev;
558+
EXPECT_GE(props.pciDeviceId, 0) << "pciDeviceId out of range for device " << dev;
559+
EXPECT_LE(props.pciDeviceId, 31) << "pciDeviceId out of range for device " << dev;
560+
EXPECT_GE(props.pciDomainId, 0) << "pciDomainId out of range for device " << dev;
561+
EXPECT_LE(props.pciDomainId, 65535) << "pciDomainId out of range for device " << dev;
562+
563+
std::cout << "Device " << dev << ": name=" << props.name
564+
<< ", pciBusId=" << props.pciBusId
565+
<< ", pciDeviceId=" << props.pciDeviceId
566+
<< ", pciDomainId=" << props.pciDomainId << std::endl;
567+
}
568+
569+
// Invalid-argument guards
570+
EXPECT_EQ(deviceAdaptor->getDeviceProperties(nullptr, 0),
571+
flagcxInvalidArgument);
572+
573+
// Out-of-range device index
574+
flagcxDevProps props = {};
575+
EXPECT_NE(deviceAdaptor->getDeviceProperties(&props, -1), flagcxSuccess);
576+
EXPECT_NE(deviceAdaptor->getDeviceProperties(&props, numDevices),
577+
flagcxSuccess);
578+
}
579+
580+
// Test: getDeviceByPciBusId — reverse lookup and invalid-arg guards
581+
TEST_F(DeviceAdaptorTest, GetDeviceByPciBusId) {
582+
int numDevices = 0;
583+
ASSERT_EQ(devHandle->getDeviceCount(&numDevices), flagcxSuccess);
584+
ASSERT_GT(numDevices, 0);
585+
586+
for (int dev = 0; dev < numDevices; dev++) {
587+
// Get PCI bus ID string directly from runtime (independent of getDevicePciBusId)
588+
char pciBusId[FLAGCX_DEVICE_PCI_BUSID_BUFFER_SIZE] = {};
589+
ASSERT_EQ(cudaDeviceGetPCIBusId(pciBusId, sizeof(pciBusId), dev), cudaSuccess)
590+
<< "cudaDeviceGetPCIBusId failed for dev=" << dev;
591+
592+
// Reverse lookup: string -> device index
593+
int result = -1;
594+
ASSERT_EQ(deviceAdaptor->getDeviceByPciBusId(&result, pciBusId), flagcxSuccess)
595+
<< "getDeviceByPciBusId failed for pci=" << pciBusId;
596+
EXPECT_EQ(result, dev)
597+
<< "round-trip mismatch: pci=" << pciBusId
598+
<< " returned dev=" << result << " expected=" << dev;
599+
600+
std::cout << "Device " << dev << ": pci=" << pciBusId
601+
<< " -> dev=" << result << std::endl;
602+
}
603+
604+
// Invalid-argument guards
605+
int dev = -1;
606+
char pciBusId[FLAGCX_DEVICE_PCI_BUSID_BUFFER_SIZE] = {};
607+
EXPECT_EQ(deviceAdaptor->getDeviceByPciBusId(nullptr, pciBusId),
608+
flagcxInvalidArgument);
609+
EXPECT_EQ(deviceAdaptor->getDeviceByPciBusId(&dev, nullptr),
610+
flagcxInvalidArgument);
611+
612+
// Invalid PCI string
613+
EXPECT_NE(deviceAdaptor->getDeviceByPciBusId(&dev, "invalid"), flagcxSuccess);
614+
}
615+
616+
// Test: eventElapsedTime reports the duration between two timing events.
617+
TEST_F(DeviceAdaptorTest, EventElapsedTime) {
618+
ASSERT_NE(devHandle->eventElapsedTime, nullptr);
619+
flagcxEvent_t startEvent = nullptr;
620+
flagcxEvent_t endEvent = nullptr;
621+
ASSERT_EQ(devHandle->eventCreate(&startEvent, flagcxEventDefault), flagcxSuccess);
622+
ASSERT_EQ(devHandle->eventCreate(&endEvent, flagcxEventDefault), flagcxSuccess);
623+
ASSERT_EQ(devHandle->eventRecord(startEvent, stream), flagcxSuccess);
624+
ASSERT_EQ(devHandle->eventRecord(endEvent, stream), flagcxSuccess);
625+
ASSERT_EQ(devHandle->eventSynchronize(endEvent), flagcxSuccess);
626+
float elapsedMs = -1.0f;
627+
EXPECT_EQ(devHandle->eventElapsedTime(&elapsedMs, startEvent, endEvent),
628+
flagcxSuccess);
629+
EXPECT_GE(elapsedMs, 0.0f);
630+
EXPECT_EQ(devHandle->eventElapsedTime(nullptr, startEvent, endEvent),
631+
flagcxInvalidArgument);
632+
EXPECT_EQ(devHandle->eventElapsedTime(&elapsedMs, nullptr, endEvent),
633+
flagcxInvalidArgument);
634+
EXPECT_EQ(devHandle->eventElapsedTime(&elapsedMs, startEvent, nullptr),
635+
flagcxInvalidArgument);
636+
EXPECT_EQ(devHandle->eventDestroy(startEvent), flagcxSuccess);
637+
EXPECT_EQ(devHandle->eventDestroy(endEvent), flagcxSuccess);
638+
}

0 commit comments

Comments
 (0)