Add support for external network.

Dynamically load external network from libnccl-net.so.
Add init function in networks.
Move PCI scoring to net.cu, only ask transport to provide a path.
Simplify CUDA PCI path detection.
Add dummy external network
This commit is contained in:
Sylvain Jeaugey
2018-11-13 10:37:20 -08:00
parent d7a58cfa58
commit 0d3a20f96d
29 changed files with 437 additions and 361 deletions
+90 -60
View File
@@ -19,11 +19,21 @@
#define NET_BITS_PER_IF 3
#define NET_BITS_PER_IF_MASK ((1<<NET_BITS_PER_IF)-1)
static_assert(sizeof(ncclTvalue_t)*8 >= NET_MAX_IFS*NET_BITS_PER_IF, "NET_MAX_IFS*NET_BITS_PER_IF must fit in a ncclTvalue_t");
static ncclTvalue_t getTvalue(short* distances, int ndev) {
ncclTvalue_t tvalue = 0;
for (int d=0; d<ndev; d++) {
int score = 1 + PATH_SOC - distances[d];
// Keep 3 bits of score info per dev
tvalue |= ((score & NET_BITS_PER_IF_MASK)<<(NET_BITS_PER_IF*d));
}
return tvalue;
}
struct netInfo {
int rank;
int ndev;
short scores[NET_MAX_IFS];
ncclTvalue_t tValue;
short distances[NET_MAX_IFS];
};
struct netConnectInfo {
@@ -38,7 +48,7 @@ struct netSendResources {
struct ncclRecvMem* devHostRecvMem;
struct ncclSendMem* hostDevMem;
int netDev;
bool cudaSupport;
int useGdr;
struct ncclRecvMem* devNetMem;
uint64_t llStep;
uint64_t llLastCleaning;
@@ -53,7 +63,7 @@ struct netRecvResources {
struct ncclRecvMem* devHostRecvMem;
struct ncclRecvMem* hostDevMem;
int netDev;
bool cudaSupport;
int useGdr;
uint64_t llStep;
uint64_t llLastCleaning;
};
@@ -64,26 +74,37 @@ ncclResult_t netFillInfo(ncclTinfo_t* opaqueInfo, int rank) {
struct netInfo* info = (struct netInfo*)opaqueInfo;
static_assert(sizeof(struct netInfo) <= sizeof(ncclTinfo_t), "NET Info too large");
info->rank = rank;
int *scores;
NCCLCHECK(ncclNetDevices(&info->ndev, &scores));
NCCLCHECK(ncclNetDevices(&info->ndev));
if (info->ndev == 0) {
WARN("Error : Network returned 0 device");
return ncclSystemError;
}
if (info->ndev > NET_MAX_IFS) info->ndev = NET_MAX_IFS;
for (int d=0; d<info->ndev; d++) info->scores[d] = scores[d];
free(scores);
// Find distance with current GPU
int cudaDev;
cudaGetDevice(&cudaDev);
char* cudaPath;
NCCLCHECK(getCudaPath(cudaDev, &cudaPath));
char line[1024];
sprintf(line, "CUDA Dev %d, %s NIC distance : ", cudaDev, ncclNetName());
for (int d=0; d<info->ndev; d++) {
char* nicPath;
ncclResult_t err = ncclNetPciPath(d, &nicPath);
info->distances[d] = (err != ncclSuccess || nicPath == NULL || cudaPath == NULL) ? PATH_SOC : pciDistance(nicPath, cudaPath);
sprintf(line+strlen(line), " %s", pathDists[info->distances[d]]);
if (err == ncclSuccess) free(nicPath);
}
INFO(NCCL_INIT|NCCL_NET, "%s", line);
free(cudaPath);
return ncclSuccess;
}
/* Determine if we can communicate with the peer */
ncclResult_t netCanConnect(ncclTvalue_t* ret, ncclTinfo_t* myOpaqueInfo, ncclTinfo_t* peerOpaqueInfo) {
ret[0] = 0;
struct netInfo* myInfo = (struct netInfo*)myOpaqueInfo;
for (int d=0; d<myInfo->ndev; d++) {
// Keep 3 bits of score info per dev
ret[0] |= ((myInfo->scores[d] & NET_BITS_PER_IF_MASK)<<(NET_BITS_PER_IF*d));
}
ret[0] = getTvalue(myInfo->distances, myInfo->ndev);
return ncclSuccess;
}
@@ -175,13 +196,13 @@ ncclResult_t netGetRings(int nranks, int* groups, int* subgroups, ncclTvalue_t*
return ncclSuccess;
}
int getDev(int ringId, int nDev, short* scores) {
int maxScore = 0;
for (int d=0; d<nDev; d++) if (scores[d] > maxScore) maxScore = scores[d];
int getDev(int ringId, int nDev, short* distances) {
int minDistance = PATH_SOC;
for (int d=0; d<nDev; d++) if (distances[d] < minDistance) minDistance = distances[d];
int skip = ringId+1;
while (skip) {
for (int d=0; d<nDev; d++) {
if (scores[d] == maxScore) {
if (distances[d] == minDistance) {
skip--;
if (skip == 0) return d;
}
@@ -191,6 +212,40 @@ int getDev(int ringId, int nDev, short* scores) {
}
NCCL_PARAM(NetGdrRead, "NET_GDR_READ", -2);
NCCL_PARAM(NetGdrLevel, "NET_GDR_LEVEL", PATH_PHB);
static ncclResult_t netGetGdrSupport(int dev, int distance, int read, int* useGdr) {
*useGdr = 0;
int cudaDev;
CUDACHECK(cudaGetDevice(&cudaDev));
if (read) { // For reads (sends) only enable under certain conditions
int gdrReadParam = ncclParamNetGdrRead();
if (gdrReadParam == 0) return ncclSuccess;
else if (gdrReadParam < 0) { // default : enable only on DGX2
char busId[NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE];
CUDACHECK(cudaDeviceGetPCIBusId(busId, NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE, cudaDev));
int nvlinks = getNumNvlinks(busId);
if (nvlinks < CONNECT_NVSWITCH || ncclCudaCompCap() < 7) return ncclSuccess;
}
}
// Check if we are close enough that it makes sense to enable GDR
int netGdrLevel = ncclParamNetGdrLevel();
if (distance >= netGdrLevel) {
INFO(NCCL_INIT|NCCL_NET,"NET/%s : GPU Direct RDMA Disabled for GPU %d / HCA %d (distance %d >= %d)", ncclNetName(), cudaDev, dev, distance, netGdrLevel);
return ncclSuccess;
}
// Finally, check if the NIC supports it
int flags;
NCCLCHECK(ncclNetPtrSupport(dev, &flags));
if (flags & NCCL_PTR_CUDA == 0) return ncclSuccess;
*useGdr = 1;
INFO(NCCL_INIT|NCCL_NET,"NET/%s : GPU Direct RDMA Enabled for GPU %d / HCA %d (distance %d >= %d), read %d", ncclNetName(), cudaDev, dev, distance, netGdrLevel, read);
return ncclSuccess;
}
/* Determine if we will use this transport for this peer and return connect
* information for this peer */
@@ -200,34 +255,11 @@ ncclResult_t netSendSetup(ncclTinfo_t* myOpaqueInfo, ncclTinfo_t* peerOpaqueInfo
ring->send.transportResources = resources;
struct netInfo* myInfo = (struct netInfo*)myOpaqueInfo;
resources->netDev = getDev(ring->id, myInfo->ndev, myInfo->scores);
resources->cudaSupport = false;
// Get user's GDR READ setting
int gdrReadParam = ncclParamNetGdrRead();
// Determine whether the GPU has NVLink
int cudaDev;
CUDACHECK(cudaGetDevice(&cudaDev));
char busId[NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE];
CUDACHECK(cudaDeviceGetPCIBusId(busId, NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE, cudaDev));
int nvlinks = getNumNvlinks(busId);
// Enable GDR read when:
// 1) user sets it, or
// 2) we are on a NVSwitch platform (i.e. no P2P traffic over PCI-E switch) AND the GPU is Volta
bool enableGdrRead = (gdrReadParam > 0) || (nvlinks >= CONNECT_NVSWITCH && ncclCudaCompCap() > 6 && gdrReadParam != 0);
if (enableGdrRead) {
int flags;
NCCLCHECK(ncclNetPtrSupport(resources->netDev, &flags));
if (flags & NCCL_PTR_CUDA)
resources->cudaSupport = true;
}
if (resources->cudaSupport)
INFO(INIT|NET, "Net: enabling net device %d to read from rank %d", resources->netDev, myInfo->rank);
resources->netDev = getDev(ring->id, myInfo->ndev, myInfo->distances);
NCCLCHECK(netGetGdrSupport(resources->netDev, myInfo->distances[resources->netDev], 1, &resources->useGdr));
int size = offsetof(struct ncclRecvMem, buff)+ring->buffSize;
if (resources->cudaSupport) {
if (resources->useGdr) {
NCCLCHECK(ncclCudaCalloc((char**)(&resources->devNetMem), size));
}
@@ -243,10 +275,8 @@ ncclResult_t netRecvSetup(ncclTinfo_t* myOpaqueInfo, ncclTinfo_t* peerOpaqueInfo
ring->recv.transportResources = resources;
struct netInfo* myInfo = (struct netInfo*)myOpaqueInfo;
resources->netDev = getDev(ring->id, myInfo->ndev, myInfo->scores);
int flags;
NCCLCHECK(ncclNetPtrSupport(resources->netDev, &flags));
resources->cudaSupport = (flags & NCCL_PTR_CUDA) ? true : false;
resources->netDev = getDev(ring->id, myInfo->ndev, myInfo->distances);
NCCLCHECK(netGetGdrSupport(resources->netDev, myInfo->distances[resources->netDev], 0, &resources->useGdr));
int sendSize = sizeof(struct ncclSendMem);
NCCLCHECK(ncclCudaHostAlloc((void**)&resources->hostSendMem, (void**)&resources->devHostSendMem, sendSize));
@@ -255,8 +285,8 @@ ncclResult_t netRecvSetup(ncclTinfo_t* myOpaqueInfo, ncclTinfo_t* peerOpaqueInfo
NCCLCHECK(ncclCudaHostAlloc((void**)&resources->hostRecvMem, (void**)&resources->devHostRecvMem, recvSize));
struct netInfo* peerInfo = (struct netInfo*)peerOpaqueInfo;
INFO(INIT|NET,"Ring %02d : %d -> %d via NET/%s/%d%s%s", ring->id, peerInfo->rank, myInfo->rank, ncclNetName(), resources->netDev,
resources->cudaSupport ? "/GDRDMA" : "",
INFO(NCCL_INIT|NCCL_NET,"Ring %02d : %d -> %d via NET/%s/%d%s%s", ring->id, peerInfo->rank, myInfo->rank, ncclNetName(), resources->netDev,
resources->useGdr ? "/GDRDMA" : "",
(resources->hostDevMem != NULL) ? "/GDCopy" : "");
struct netConnectInfo* info = (struct netConnectInfo*) connectInfo;
NCCLCHECK(ncclNetListen(resources->netDev, &info->netHandle, &resources->netListenComm));
@@ -267,7 +297,7 @@ ncclResult_t netSendConnect(struct ncclConnect* connectInfo, struct ncclConnecto
// Setup device pointers
struct netSendResources* resources = (struct netSendResources*)send->transportResources;
if (resources->cudaSupport) {
if (resources->useGdr) {
send->conn.buff = resources->devNetMem->buff;
// We don't use devMem for llMode because the CPU has to read the data
send->conn.llBuff = resources->devHostRecvMem->llBuff;
@@ -299,7 +329,7 @@ ncclResult_t netRecvConnect(struct ncclConnect* connectInfo, struct ncclConnecto
recv->conn.head = &resources->devHostSendMem->head;
recv->conn.llHead = &resources->devHostSendMem->llHead;
if (resources->cudaSupport == false) {
if (resources->useGdr == 0) {
recv->conn.buff = resources->devHostRecvMem->buff;
recv->conn.llBuff = resources->devHostRecvMem->llBuff;
}
@@ -320,7 +350,7 @@ ncclResult_t netSendFree(void* transportResources) {
struct netSendResources* resources = (struct netSendResources*)transportResources;
NCCLCHECK(ncclCudaHostFree(resources->hostSendMem));
NCCLCHECK(ncclCudaHostFree(resources->hostRecvMem));
if (resources->cudaSupport)
if (resources->useGdr)
CUDACHECK(cudaFree(resources->devNetMem));
NCCLCHECK(ncclNetCloseSend(resources->netSendComm));
free(resources);
@@ -344,9 +374,9 @@ ncclResult_t netSendProxy(struct ncclProxyArgs* args) {
volatile uint64_t* prevTail = &resources->hostRecvMem->tail;
struct ncclSendMem* prevMem = resources->hostDevMem ? resources->hostDevMem : resources->hostSendMem;
uint64_t* prevHead = llMode ? &prevMem->llHead : &prevMem->head;
struct ncclRecvMem* localMem = resources->cudaSupport ? resources->devNetMem : resources->hostRecvMem;
struct ncclRecvMem* localMem = resources->useGdr ? resources->devNetMem : resources->hostRecvMem;
char* localBuff = llMode ? resources->hostRecvMem->llBuff : localMem->buff;
int ptrType = resources->cudaSupport ? NCCL_PTR_CUDA : NCCL_PTR_HOST;
int ptrType = resources->useGdr ? NCCL_PTR_CUDA : NCCL_PTR_HOST;
volatile int* sizesFifo = llMode ? resources->hostRecvMem->llSizesFifo : resources->hostRecvMem->sizesFifo;
int buffSize = llMode ? NCCL_LL_BUFF_SIZE : ring->buffSize;
int sliceSize = buffSize / args->substeps;
@@ -362,8 +392,8 @@ ncclResult_t netSendProxy(struct ncclProxyArgs* args) {
if (!args->needProxy) goto nextColl;
TRACE(NET,"opCount %lx head %lx tail %lx end %lx nsteps %d llMode %d", args->opCount, head, tail, end, args->nsteps, llMode);
TRACE(NET,"opCount %lx buffSize %d sliceSize %d ptrType %d", args->opCount, buffSize, sliceSize, ptrType);
TRACE(NCCL_NET,"opCount %lx head %lx tail %lx end %lx nsteps %d llMode %d", args->opCount, head, tail, end, args->nsteps, llMode);
TRACE(NCCL_NET,"opCount %lx buffSize %d sliceSize %d ptrType %d", args->opCount, buffSize, sliceSize, ptrType);
// Update in case we skipped some collectives
if (llMode == 0) resources->hostRecvMem->opCount = args->opCount;
@@ -440,10 +470,10 @@ ncclResult_t netRecvProxy(struct ncclProxyArgs* args) {
int llMode = args->llMode;
volatile uint64_t* nextHead = llMode ? &resources->hostSendMem->llHead : &resources->hostSendMem->head;
struct ncclRecvMem* localMem = resources->cudaSupport ? ring->devMemRecv : resources->hostRecvMem;
struct ncclRecvMem* localMem = resources->useGdr ? ring->devMemRecv : resources->hostRecvMem;
char* localBuff = llMode ? localMem->llBuff : localMem->buff;
char* nextBuff = (resources->cudaSupport == false && resources->hostDevMem) ? resources->hostDevMem->buff : NULL;
int ptrType = resources->cudaSupport ? NCCL_PTR_CUDA : NCCL_PTR_HOST;
char* nextBuff = (resources->useGdr == 0 && resources->hostDevMem) ? resources->hostDevMem->buff : NULL;
int ptrType = resources->useGdr ? NCCL_PTR_CUDA : NCCL_PTR_HOST;
uint64_t* nextTail = resources->hostDevMem ? &resources->hostDevMem->tail : &resources->hostRecvMem->tail;
int buffSize = llMode ? NCCL_LL_BUFF_SIZE : ring->buffSize;
@@ -458,8 +488,8 @@ ncclResult_t netRecvProxy(struct ncclProxyArgs* args) {
if (!args->needProxy) goto nextColl;
TRACE(NET,"opCount %lx head %lx tail %lx end %lx nsteps %d llMode %d", args->opCount, head, tail, end, args->nsteps, llMode);
TRACE(NET,"opCount %lx buffSize %d sliceSize %d ptrType %d", args->opCount, buffSize, sliceSize, ptrType);
TRACE(NCCL_NET,"opCount %lx head %lx tail %lx end %lx nsteps %d llMode %d", args->opCount, head, tail, end, args->nsteps, llMode);
TRACE(NCCL_NET,"opCount %lx buffSize %d sliceSize %d ptrType %d", args->opCount, buffSize, sliceSize, ptrType);
if (llMode == 0) {
// Waiting for next opCount is only needed before writing nextTail.
+34 -69
View File
@@ -82,8 +82,12 @@ static void* ncclIbAsyncThreadMain(void* args) {
return NULL;
}
static void initDevices() {
if(wrap_ibv_symbols() != ncclSuccess) { return; }
NCCL_PARAM(IbDisable, "IB_DISABLE", 0);
ncclResult_t ncclIbInit(ncclDebugLogger_t logFunction) {
if(wrap_ibv_symbols() != ncclSuccess) { return ncclInternalError; }
if (ncclParamIbDisable()) return ncclInternalError;
if (ncclNIbDevs == -1) {
pthread_mutex_lock(&ncclIbLock);
wrap_ibv_fork_init();
@@ -91,9 +95,9 @@ static void initDevices() {
ncclNIbDevs = 0;
if (findInterfaces(ncclIbIfName, &ncclIbIfAddr, MAX_IF_NAME_SIZE, 1) != 1) {
WARN("NET/IB : No IP interface found.");
return;
return ncclInternalError;
}
INFO(INIT|NET,"NET/IB : Using interface %s for sideband communication", ncclIbIfName);
INFO(NCCL_INIT|NCCL_NET,"NET/IB : Using interface %s for sideband communication", ncclIbIfName);
// Detect IB cards
int nIbDevs;
@@ -105,7 +109,7 @@ static void initDevices() {
bool searchNot = userIbEnv && userIbEnv[0] == '^';
int nUserIfs = parseStringList(userIbEnv, userIfs, MAX_IB_DEVS);
if (ncclSuccess != wrap_ibv_get_device_list(&devices, &nIbDevs)) return;
if (ncclSuccess != wrap_ibv_get_device_list(&devices, &nIbDevs)) return ncclInternalError;
for (int d=0; d<nIbDevs; d++) {
struct ibv_context * context;
@@ -134,7 +138,7 @@ static void initDevices() {
if (! (matchIfList(devices[d]->name, port, userIfs, nUserIfs) ^ searchNot)) {
continue;
}
INFO(INIT|NET,"NET/IB: [%d] %s:%d/%s ", d, devices[d]->name, port,
INFO(NCCL_INIT|NCCL_NET,"NET/IB: [%d] %s:%d/%s ", d, devices[d]->name, port,
portAttr.link_layer == IBV_LINK_LAYER_INFINIBAND ? "IB" : "RoCE");
ncclIbDevs[ncclNIbDevs].device = d;
ncclIbDevs[ncclNIbDevs].port = port;
@@ -145,38 +149,29 @@ static void initDevices() {
pthread_create(&ncclIbAsyncThread, NULL, ncclIbAsyncThreadMain, context);
}
if (found == 0) { if (ncclSuccess != wrap_ibv_close_device(context)) { return; } }
if (found == 0) { if (ncclSuccess != wrap_ibv_close_device(context)) { return ncclInternalError; } }
}
}
if (nIbDevs && (ncclSuccess != wrap_ibv_free_device_list(devices))) { return; };
if (nIbDevs && (ncclSuccess != wrap_ibv_free_device_list(devices))) { return ncclInternalError; };
}
pthread_mutex_unlock(&ncclIbLock);
}
return ncclSuccess;
}
ncclResult_t ncclIbDevices(int* ndev, int** scores) {
initDevices();
ncclResult_t ncclIbDevices(int* ndev) {
*ndev = ncclNIbDevs;
int cudaDev;
cudaGetDevice(&cudaDev);
char* cudaPath;
ncclResult_t err1 = getCudaPath(cudaDev, &cudaPath);
int* sc;
NCCLCHECK(ncclCalloc(&sc, ncclNIbDevs));
char line[1024];
sprintf(line, "CUDA Dev %d, IB Ports : ", cudaDev);
for (int d=0; d<ncclNIbDevs; d++) {
char* mlxPath;
ncclResult_t err2 = getMlxPath(ncclIbDevs[d].devName, &mlxPath);
int distance = (err1 != ncclSuccess || err2 != ncclSuccess || mlxPath == NULL || cudaPath == NULL) ? PATH_SOC : pciDistance(mlxPath, cudaPath);
sprintf(line+strlen(line), "%s/%d(%s) ", ncclIbDevs[d].devName, ncclIbDevs[d].port, pathDists[distance]);
sc[d] = 1+PATH_SOC-distance;
if (err2 == ncclSuccess) free(mlxPath);
return ncclSuccess;
}
ncclResult_t ncclIbPciPath(int dev, char** path) {
char devicepath[PATH_MAX];
snprintf(devicepath, PATH_MAX, "/sys/class/infiniband/%s/device", ncclIbDevs[dev].devName);
*path = realpath(devicepath, NULL);
if (*path == NULL) {
WARN("Could not find real path of %s", devicepath);
return ncclSystemError;
}
INFO(INIT|NET,"%s", line);
if (err1 == ncclSuccess) free(cudaPath);
*scores = sc;
return ncclSuccess;
}
@@ -207,45 +202,21 @@ ncclResult_t ncclIbGdrSupport(int ibDev) {
return ret;
}
NCCL_PARAM(IbGdrLevel, "IB_GDR_LEVEL", -2);
NCCL_PARAM(IbCudaSupport, "IB_CUDA_SUPPORT", -2);
ncclResult_t ncclIbPtrSupport(int dev, int* supportedTypes) {
initDevices();
*supportedTypes = NCCL_PTR_HOST;
int cudaDev;
if (cudaGetDevice(&cudaDev) != cudaSuccess) return ncclSuccess;
CUDACHECK(cudaGetDevice(&cudaDev));
int ibGdrLevel = PATH_PHB;
if (ncclParamIbCudaSupport() != -2) ibGdrLevel = ncclParamIbCudaSupport() ? PATH_SOC + 1 : 0;
if (ncclParamIbGdrLevel() != -2) ibGdrLevel = ncclParamIbGdrLevel();
if (ibGdrLevel > 0) {
int gdrSupport = ncclIbGdrSupport(dev);
if (gdrSupport > 0) {
INFO(INIT|NET,"NET/IB : GPU Direct RDMA Disabled for GPU %d / HCA %s (%s)", cudaDev, ncclIbDevs[dev].devName, gdrSupport == 1 ? "no module" : "not supported by GPU");
ibGdrLevel = 0;
}
}
if (ibGdrLevel <= 0) return ncclSuccess;
char* cudaPath;
if (getCudaPath(cudaDev, &cudaPath) != ncclSuccess) return ncclSuccess;
char* mlxPath;
if (getMlxPath(ncclIbDevs[dev].devName, &mlxPath) != ncclSuccess) { free(cudaPath); return ncclSuccess; }
int distance = (mlxPath == NULL || cudaPath == NULL) ? PATH_SOC : pciDistance(mlxPath, cudaPath);
free(mlxPath); free(cudaPath);
if (distance < ibGdrLevel) {
*supportedTypes |= NCCL_PTR_CUDA;
} else {
INFO(INIT|NET,"NET/IB : GPU Direct RDMA Disabled for GPU %d / HCA %s (distance %d >= %d)", cudaDev, ncclIbDevs[dev].devName, distance, ibGdrLevel);
if (ncclIbGdrSupport(dev) != ncclSuccess) {
INFO(NCCL_INIT|NCCL_NET,"NET/IB : GPU Direct RDMA Disabled for GPU %d / HCA %s (no module or not supported by GPU)", cudaDev, ncclIbDevs[dev].devName);
return ncclSuccess;
}
*supportedTypes |= NCCL_PTR_CUDA;
return ncclSuccess;
}
static ncclResult_t GetSocketAddr(union socketAddress* addr) {
if (ncclNIbDevs == -1) initDevices();
memcpy(addr, &ncclIbIfAddr, sizeof(*addr));
return ncclSuccess;
}
@@ -442,7 +413,6 @@ ncclResult_t ncclIbConnect(int dev, void* opaqueHandle, void** sendComm) {
*sendComm = comm;
// IB Setup
initDevices(); /*NOTE: We need to do this for ncclNet unit test that bypasses nccl initialization*/
ibv_context* ctx = ncclIbDevs[dev].context;
NCCLCHECK(ncclIbInitVerbs(ctx, &comm->verbs));
uint8_t ib_port = ncclIbDevs[dev].port;
@@ -464,13 +434,13 @@ ncclResult_t ncclIbConnect(int dev, void* opaqueHandle, void** sendComm) {
// RoCE support
qpInfo.lid = portAttr.lid;
if (qpInfo.lid) { // IB
INFO(INIT|NET,"NET/IB: Dev %d Port %d qpn %d mtu %d LID %d", dev, ib_port, qpInfo.qpn, qpInfo.mtu, qpInfo.lid);
INFO(NCCL_INIT|NCCL_NET,"NET/IB: Dev %d Port %d qpn %d mtu %d LID %d", dev, ib_port, qpInfo.qpn, qpInfo.mtu, qpInfo.lid);
} else { // RoCE
union ibv_gid gid;
NCCLCHECK(wrap_ibv_query_gid(ctx, ib_port, ncclParamIbGidIndex(), &gid));
qpInfo.spn = gid.global.subnet_prefix;
qpInfo.iid = gid.global.interface_id;
INFO(INIT|NET,"NET/IB: Dev %d Port %d qpn %d mtu %d GID %ld (%lX/%lX)", dev, ib_port, qpInfo.qpn, qpInfo.mtu, ncclParamIbGidIndex(), qpInfo.spn, qpInfo.iid);
INFO(NCCL_INIT|NCCL_NET,"NET/IB: Dev %d Port %d qpn %d mtu %d GID %ld (%lX/%lX)", dev, ib_port, qpInfo.qpn, qpInfo.mtu, ncclParamIbGidIndex(), qpInfo.spn, qpInfo.iid);
}
NCCLCHECK(socketSend(comm->fd, &qpInfo, sizeof(qpInfo)));
@@ -649,7 +619,7 @@ ncclResult_t ncclIbGetMr(struct ncclIbVerbs* verbs, void* data, int size, struct
NCCLCHECK(wrap_ibv_reg_mr(&verbs->mrPool[elem].mr, verbs->pd, (void*)regAddr, regSize, IBV_ACCESS_LOCAL_WRITE|IBV_ACCESS_REMOTE_WRITE|IBV_ACCESS_REMOTE_READ));
*mrRet = verbs->mrPool+elem;
verbs->mrPool[elem].refcnt++;
TRACE(INIT,"elem %d regAddr %lx size %ld rkey %x", elem, regAddr, regSize, (verbs->mrPool+elem)->mr->rkey);
TRACE(NCCL_INIT,"elem %d regAddr %lx size %ld rkey %x", elem, regAddr, regSize, (verbs->mrPool+elem)->mr->rkey);
return ncclSuccess;
}
@@ -903,7 +873,9 @@ ncclResult_t ncclIbCloseListen(void* listenComm) {
ncclNet_t ncclNetIb = {
"IB",
ncclIbInit,
ncclIbDevices,
ncclIbPciPath,
ncclIbPtrSupport,
ncclIbListen,
ncclIbConnect,
@@ -917,10 +889,3 @@ ncclNet_t ncclNetIb = {
ncclIbCloseListen
};
NCCL_PARAM(IbDisable, "IB_DISABLE", 0);
bool ncclIbSupport() {
if (ncclParamIbDisable()) return 0;
initDevices();
return ncclNIbDevs > 0;
}
+33 -40
View File
@@ -8,67 +8,58 @@
#include "core.h"
#include "socket.h"
#include "net.h"
#include "topo.h"
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include <limits.h>
/* Init functions */
static char ncclNetIfNames[MAX_IF_NAME_SIZE*MAX_IFS];
static union socketAddress ncclNetIfAddrs[MAX_IFS];
static int ncclNetIfs = -1;
pthread_mutex_t ncclSocketLock = PTHREAD_MUTEX_INITIALIZER;
ncclResult_t ncclSocketInit(ncclDebugLogger_t logFunction) {
if (ncclNetIfs == -1) {
pthread_mutex_lock(&ncclSocketLock);
if (ncclNetIfs == -1) {
ncclNetIfs = findInterfaces(ncclNetIfNames, ncclNetIfAddrs, MAX_IF_NAME_SIZE, MAX_IFS);
INFO(NCCL_INIT|NCCL_NET,"NET/Socket : %d interfaces found", ncclNetIfs);
if (ncclNetIfs <= 0) {
WARN("NET/Socket : no interface found");
return ncclInternalError;
}
}
pthread_mutex_unlock(&ncclSocketLock);
}
return ncclSuccess;
}
ncclResult_t ncclSocketPtrSupport(int dev, int* supportedTypes) {
*supportedTypes = NCCL_PTR_HOST;
return ncclSuccess;
}
static char ncclNetIfNames[MAX_IF_NAME_SIZE*MAX_IFS];
static union socketAddress ncclNetIfAddrs[MAX_IFS];
static int ncclNetIfs = -1;
pthread_mutex_t ncclSocketLock = PTHREAD_MUTEX_INITIALIZER;
static void initDevices() {
if (ncclNetIfs == -1) {
pthread_mutex_lock(&ncclSocketLock);
if (ncclNetIfs == -1) {
ncclNetIfs = findInterfaces(ncclNetIfNames, ncclNetIfAddrs, MAX_IF_NAME_SIZE, MAX_IFS);
INFO(INIT|NET,"NET/Socket : %d interfaces found", ncclNetIfs);
if (ncclNetIfs <= 0) {
WARN("NET/Socket : no interface found");
}
}
pthread_mutex_unlock(&ncclSocketLock);
}
ncclResult_t ncclSocketDevices(int* ndev) {
*ndev = ncclNetIfs;
return ncclSuccess;
}
ncclResult_t ncclSocketDevices(int* ndev, int** scores) {
initDevices();
*ndev = ncclNetIfs;
int cudaDev;
cudaGetDevice(&cudaDev);
char* cudaPath;
ncclResult_t err1 = getCudaPath(cudaDev, &cudaPath);
int* sc;
NCCLCHECK(ncclCalloc(&sc, ncclNetIfs));
char line[1024];
sprintf(line, "CUDA Dev %d, IP Interfaces : ", cudaDev);
for (int i=0; i<ncclNetIfs; i++) {
char* sockPath;
ncclResult_t err2 = getSockPath(ncclNetIfNames+i*MAX_IF_NAME_SIZE, &sockPath);
int distance = (err1 != ncclSuccess || err2 != ncclSuccess || sockPath == NULL || cudaPath == NULL) ? PATH_SOC : pciDistance(sockPath, cudaPath);
sprintf(line+strlen(line), "%s(%s) ", ncclNetIfNames+i*MAX_IF_NAME_SIZE, pathDists[distance]);
sc[i] = 1+PATH_SOC-distance;
if (err2 == ncclSuccess) free(sockPath);
ncclResult_t ncclSocketPciPath(int dev, char** path) {
char devicepath[PATH_MAX];
snprintf(devicepath, PATH_MAX, "/sys/class/net/%s/device", ncclNetIfNames+dev*MAX_IF_NAME_SIZE);
*path = realpath(devicepath, NULL);
if (*path == NULL) {
INFO(NCCL_NET|NCCL_INIT, "Could not find real path of %s", devicepath);
return ncclSystemError;
}
INFO(INIT|NET,"%s", line);
if (err1 == ncclSuccess) free(cudaPath);
*scores = sc;
return ncclSuccess;
}
static ncclResult_t GetSocketAddr(int dev, union socketAddress* addr) {
if (ncclNetIfs == -1) initDevices();
if (dev >= ncclNetIfs) return ncclInternalError;
memcpy(addr, ncclNetIfAddrs+dev, sizeof(*addr));
return ncclSuccess;
@@ -223,7 +214,9 @@ ncclResult_t ncclSocketClose(void* opaqueComm) {
ncclNet_t ncclNetSocket = {
"Socket",
ncclSocketInit,
ncclSocketDevices,
ncclSocketPciPath,
ncclSocketPtrSupport,
ncclSocketListen,
ncclSocketConnect,
+7 -7
View File
@@ -85,7 +85,7 @@ ncclResult_t p2pCanConnect(ncclTvalue_t* ret, ncclTinfo_t* myOpaqueInfo, ncclTin
// See if CUDA can do P2P
int p2p;
if (cudaDeviceCanAccessPeer(&p2p, myInfo->cudaDev, peerInfo->cudaDev) != cudaSuccess) {
INFO(INIT|P2P,"peer query failed between dev %d and dev %d",
INFO(NCCL_INIT|NCCL_P2P,"peer query failed between dev %d and dev %d",
myInfo->cudaDev, peerInfo->cudaDev);
return ncclSuccess;
}
@@ -454,7 +454,7 @@ ncclResult_t p2pSendSetup(ncclTinfo_t* myOpaqueInfo, ncclTinfo_t* peerOpaqueInfo
info.direct = 1;
info.directPtr = ring->devMemSend;
if (myInfo->cudaDev == peerInfo->cudaDev) {
INFO(INIT|P2P,"Ring %02d : %d -> %d via P2P/common device", ring->id, myInfo->rank, peerInfo->rank);
INFO(NCCL_INIT|NCCL_P2P,"Ring %02d : %d -> %d via P2P/common device", ring->id, myInfo->rank, peerInfo->rank);
} else {
// Enable P2P access
cudaError_t err = cudaDeviceEnablePeerAccess(peerInfo->cudaDev, 0);
@@ -465,7 +465,7 @@ ncclResult_t p2pSendSetup(ncclTinfo_t* myOpaqueInfo, ncclTinfo_t* peerOpaqueInfo
peerInfo->cudaDev, err, cudaGetErrorString(err));
return ncclInternalError;
}
INFO(INIT|P2P,"Ring %02d : %d[%d] -> %d[%d] via P2P/direct pointer",
INFO(NCCL_INIT|NCCL_P2P,"Ring %02d : %d[%d] -> %d[%d] via P2P/direct pointer",
ring->id, myInfo->rank, myInfo->cudaDev, peerInfo->rank, peerInfo->cudaDev);
}
} else {
@@ -477,7 +477,7 @@ ncclResult_t p2pSendSetup(ncclTinfo_t* myOpaqueInfo, ncclTinfo_t* peerOpaqueInfo
myInfo->rank, peerInfo->cudaDev, err, cudaGetErrorString(err));
return ncclInternalError;
}
INFO(INIT|P2P,"Ring %02d : %d[%d] -> %d[%d] via P2P/IPC",
INFO(NCCL_INIT|NCCL_P2P,"Ring %02d : %d[%d] -> %d[%d] via P2P/IPC",
ring->id, myInfo->rank, myInfo->cudaDev, peerInfo->rank, peerInfo->cudaDev);
//TRACE_DUMP_IPC(&info.devIpc);
}
@@ -495,7 +495,7 @@ ncclResult_t p2pRecvSetup(ncclTinfo_t* myOpaqueInfo, ncclTinfo_t* peerOpaqueInfo
info.direct = 1;
info.directPtr = ring->devMemRecv;
if (myInfo->cudaDev == peerInfo->cudaDev) {
TRACE(INIT|P2P,"%d <- %d via P2P/common device", myInfo->rank, peerInfo->rank);
TRACE(NCCL_INIT|NCCL_P2P,"%d <- %d via P2P/common device", myInfo->rank, peerInfo->rank);
} else {
// Enable P2P access
cudaError_t err = cudaDeviceEnablePeerAccess(peerInfo->cudaDev, 0);
@@ -506,7 +506,7 @@ ncclResult_t p2pRecvSetup(ncclTinfo_t* myOpaqueInfo, ncclTinfo_t* peerOpaqueInfo
peerInfo->cudaDev, err, cudaGetErrorString(err));
return ncclInternalError;
}
TRACE(INIT|P2P,"Ring %02d : %d[%d] <- %d[%d] via P2P/direct pointer", ring->id, myInfo->rank, myInfo->cudaDev, peerInfo->rank, peerInfo->cudaDev);
TRACE(NCCL_INIT|NCCL_P2P,"Ring %02d : %d[%d] <- %d[%d] via P2P/direct pointer", ring->id, myInfo->rank, myInfo->cudaDev, peerInfo->rank, peerInfo->cudaDev);
}
} else {
info.direct = 0;
@@ -517,7 +517,7 @@ ncclResult_t p2pRecvSetup(ncclTinfo_t* myOpaqueInfo, ncclTinfo_t* peerOpaqueInfo
myInfo->rank, peerInfo->cudaDev, err, cudaGetErrorString(err));
return ncclInternalError;
}
TRACE(INIT|P2P,"Ring %02d : %d[%d] <- %d[%d] via P2P/IPC", ring->id, myInfo->rank, myInfo->cudaDev, peerInfo->rank, peerInfo->cudaDev);
TRACE(NCCL_INIT|NCCL_P2P,"Ring %02d : %d[%d] <- %d[%d] via P2P/IPC", ring->id, myInfo->rank, myInfo->cudaDev, peerInfo->rank, peerInfo->cudaDev);
//TRACE_DUMP_IPC(&info.devIpc);
}
static_assert(sizeof(struct p2pConnectInfo) <= sizeof(struct ncclConnect), "p2p Connect Info is too big");
+5 -5
View File
@@ -168,10 +168,10 @@ ncclResult_t shmSendSetup(ncclTinfo_t* myOpaqueInfo, ncclTinfo_t* peerOpaqueInfo
char shmName[MAX_SHM_NAME_LEN];
sprintf(shmName, "nccl-shm-send-%lx-%d-%d", myInfo->pidHash, ring->id, myInfo->rank);
info.shmSize = resources->shmSize = sizeof(struct ncclSendMem);
TRACE(SHM,"Open shmName %s shmSize %d", shmName, info.shmSize);
TRACE(NCCL_SHM,"Open shmName %s shmSize %d", shmName, info.shmSize);
NCCLCHECK(shmOpen(shmName, resources->shmSize, (void**)&resources->hostMem, (void**)&resources->devHostMem, 1));
INFO(INIT|SHM,"Ring %02d : %d[%d] -> %d[%d] via direct shared memory", ring->id, myInfo->rank, myInfo->cudaDev, peerInfo->rank, peerInfo->cudaDev);
INFO(NCCL_INIT|NCCL_SHM,"Ring %02d : %d[%d] -> %d[%d] via direct shared memory", ring->id, myInfo->rank, myInfo->cudaDev, peerInfo->rank, peerInfo->cudaDev);
info.id = ring->id; info.rank = myInfo->rank; info.pidHash = myInfo->pidHash;
static_assert(sizeof(struct shmRecvConnectInfo) <= sizeof(struct ncclConnect), "shm Connect Recv Info is too big");
memcpy(connectInfo, &info, sizeof(struct shmRecvConnectInfo));
@@ -189,7 +189,7 @@ ncclResult_t shmRecvSetup(ncclTinfo_t* myOpaqueInfo, ncclTinfo_t* peerOpaqueInfo
char shmName[MAX_SHM_NAME_LEN];
sprintf(shmName, "nccl-shm-recv-%lx-%d-%d", myInfo->pidHash, ring->id, myInfo->rank);
info.shmSize = resources->shmSize = offsetof(struct ncclRecvMem, buff)+ring->buffSize;
TRACE(SHM,"Open shmName %s shmSize %d", shmName, info.shmSize);
TRACE(NCCL_SHM,"Open shmName %s shmSize %d", shmName, info.shmSize);
NCCLCHECK(shmOpen(shmName, resources->shmSize, (void**)&resources->hostMem, (void**)&resources->devHostMem, 1));
info.id = ring->id; info.rank = myInfo->rank; info.pidHash = myInfo->pidHash;
@@ -207,7 +207,7 @@ ncclResult_t shmSendConnect(struct ncclConnect* connectInfo, struct ncclConnecto
char shmName[MAX_SHM_NAME_LEN];
sprintf(shmName, "nccl-shm-recv-%lx-%d-%d", info->pidHash, info->id, info->rank);
resources->remShmSize = info->shmSize;
TRACE(SHM,"Open shmName %s shmSize %d", shmName, info->shmSize);
TRACE(NCCL_SHM,"Open shmName %s shmSize %d", shmName, info->shmSize);
NCCLCHECK(shmOpen(shmName, resources->remShmSize, (void**)&resources->remHostMem, (void**)&resources->devRemHostMem, 0));
// Remove the file to ensure proper clean-up
NCCLCHECK(shmUnlink(shmName));
@@ -231,7 +231,7 @@ ncclResult_t shmRecvConnect(struct ncclConnect* connectInfo, struct ncclConnecto
char shmName[MAX_SHM_NAME_LEN];
sprintf(shmName, "nccl-shm-send-%lx-%d-%d", info->pidHash, info->id, info->rank);
resources->remShmSize = info->shmSize;
TRACE(SHM,"Open shmName %s shmSize %d", shmName, info->shmSize);
TRACE(NCCL_SHM,"Open shmName %s shmSize %d", shmName, info->shmSize);
NCCLCHECK(shmOpen(shmName, resources->remShmSize, (void**)&resources->remHostMem, (void**)&resources->devRemHostMem, 0));
NCCLCHECK(shmUnlink(shmName));
recv->conn.head = &resources->devRemHostMem->head;