2018-09-24 16:06:59 -07:00
|
|
|
/*************************************************************************
|
2021-04-12 16:00:11 -07:00
|
|
|
* Copyright (c) 2016-2021, NVIDIA CORPORATION. All rights reserved.
|
2018-09-24 16:06:59 -07:00
|
|
|
*
|
|
|
|
|
* See LICENSE.txt for license information
|
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "nccl.h"
|
|
|
|
|
#include "core.h"
|
|
|
|
|
#include "socket.h"
|
|
|
|
|
#include "net.h"
|
2019-11-19 14:57:39 -08:00
|
|
|
#include "graph.h"
|
2018-09-24 16:06:59 -07:00
|
|
|
#include "utils.h"
|
|
|
|
|
#include "param.h"
|
|
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <poll.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
#include "ibvwrap.h"
|
|
|
|
|
|
|
|
|
|
#define USE_RDMA_WRITE 1
|
|
|
|
|
#define MAXNAMESIZE 64
|
2020-09-04 14:35:05 -07:00
|
|
|
static char ncclIbIfName[MAX_IF_NAME_SIZE+1];
|
2018-09-24 16:06:59 -07:00
|
|
|
static union socketAddress ncclIbIfAddr;
|
2020-01-16 16:02:42 -08:00
|
|
|
|
2018-09-24 16:06:59 -07:00
|
|
|
static int ncclNIbDevs = -1;
|
|
|
|
|
struct ncclIbDev {
|
|
|
|
|
int device;
|
2020-01-16 16:02:42 -08:00
|
|
|
uint64_t guid;
|
2018-09-24 16:06:59 -07:00
|
|
|
uint8_t port;
|
2018-12-13 15:56:12 -08:00
|
|
|
uint8_t link;
|
2020-01-16 16:02:42 -08:00
|
|
|
int speed;
|
2018-09-24 16:06:59 -07:00
|
|
|
ibv_context* context;
|
|
|
|
|
char devName[MAXNAMESIZE];
|
2020-01-16 16:02:42 -08:00
|
|
|
char* pciPath;
|
|
|
|
|
int realPort;
|
|
|
|
|
int maxQp;
|
2018-09-24 16:06:59 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MAX_IB_PORT 15
|
|
|
|
|
struct userIbDev {
|
|
|
|
|
char devName[MAXNAMESIZE];
|
|
|
|
|
uint16_t port_en;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define MAX_IB_DEVS 16
|
|
|
|
|
struct ncclIbDev ncclIbDevs[MAX_IB_DEVS];
|
|
|
|
|
struct userIbDev userIbDevs[MAX_IB_DEVS];
|
|
|
|
|
pthread_mutex_t ncclIbLock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
|
|
|
|
NCCL_PARAM(IbGidIndex, "IB_GID_INDEX", 0);
|
|
|
|
|
NCCL_PARAM(IbTimeout, "IB_TIMEOUT", 14);
|
|
|
|
|
NCCL_PARAM(IbRetryCnt, "IB_RETRY_CNT", 7);
|
2020-09-04 14:35:05 -07:00
|
|
|
NCCL_PARAM(IbPkey, "IB_PKEY", 0);
|
|
|
|
|
NCCL_PARAM(IbUseInline, "IB_USE_INLINE", 0);
|
2018-09-24 16:06:59 -07:00
|
|
|
NCCL_PARAM(IbSl, "IB_SL", 0);
|
|
|
|
|
NCCL_PARAM(IbTc, "IB_TC", 0);
|
2020-01-16 16:02:42 -08:00
|
|
|
NCCL_PARAM(IbArThreshold, "IB_AR_THRESHOLD", 8192);
|
2018-09-24 16:06:59 -07:00
|
|
|
|
|
|
|
|
pthread_t ncclIbAsyncThread;
|
|
|
|
|
static void* ncclIbAsyncThreadMain(void* args) {
|
|
|
|
|
struct ibv_context* context = (struct ibv_context*)args;
|
|
|
|
|
while (1) {
|
|
|
|
|
struct ibv_async_event event;
|
|
|
|
|
if (ncclSuccess != wrap_ibv_get_async_event(context, &event)) { break; }
|
|
|
|
|
char *str;
|
|
|
|
|
if (ncclSuccess != wrap_ibv_event_type_str(&str, event.event_type)) { break; }
|
|
|
|
|
if (event.event_type != IBV_EVENT_COMM_EST)
|
|
|
|
|
WARN("NET/IB : Got async event : %s", str);
|
|
|
|
|
if (ncclSuccess != wrap_ibv_ack_async_event(&event)) { break; }
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-13 10:37:20 -08:00
|
|
|
NCCL_PARAM(IbDisable, "IB_DISABLE", 0);
|
|
|
|
|
|
2020-01-16 16:02:42 -08:00
|
|
|
static ncclResult_t ncclIbGetPciPath(char* devName, char** path, int* realPort) {
|
|
|
|
|
char devicePath[PATH_MAX];
|
|
|
|
|
snprintf(devicePath, PATH_MAX, "/sys/class/infiniband/%s/device", devName);
|
|
|
|
|
char* p = realpath(devicePath, NULL);
|
|
|
|
|
if (p == NULL) {
|
2021-02-09 15:34:08 -08:00
|
|
|
WARN("Could not find real path of %s (%s)", devName, devicePath);
|
2020-01-16 16:02:42 -08:00
|
|
|
} else {
|
|
|
|
|
// Merge multi-port NICs into the same PCI device
|
|
|
|
|
p[strlen(p)-1] = '0';
|
|
|
|
|
// And keep the real port aside (the ibv port is always 1 on recent cards)
|
|
|
|
|
*realPort = 0;
|
|
|
|
|
for (int d=0; d<ncclNIbDevs; d++) {
|
|
|
|
|
if (strcmp(p, ncclIbDevs[d].pciPath) == 0) (*realPort)++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*path = p;
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ibvWidths[] = { 1, 4, 8, 12 };
|
|
|
|
|
static int ibvSpeeds[] = { 2500, 5000, 10000, 10000, 14000, 25000, 50000 };
|
|
|
|
|
static int firstBitSet(int val, int max) {
|
|
|
|
|
int i = 0;
|
|
|
|
|
while (i<max && ((val & (1<<i)) == 0)) i++;
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
static int ncclIbWidth(int width) {
|
|
|
|
|
return ibvWidths[firstBitSet(width, sizeof(ibvWidths)/sizeof(int)-1)];
|
|
|
|
|
}
|
|
|
|
|
static int ncclIbSpeed(int speed) {
|
|
|
|
|
return ibvSpeeds[firstBitSet(speed, sizeof(ibvSpeeds)/sizeof(int)-1)];
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-13 10:37:20 -08:00
|
|
|
ncclResult_t ncclIbInit(ncclDebugLogger_t logFunction) {
|
2020-05-12 14:40:18 -07:00
|
|
|
static int shownIbHcaEnv = 0;
|
2018-11-13 10:37:20 -08:00
|
|
|
if(wrap_ibv_symbols() != ncclSuccess) { return ncclInternalError; }
|
|
|
|
|
if (ncclParamIbDisable()) return ncclInternalError;
|
|
|
|
|
|
2018-09-24 16:06:59 -07:00
|
|
|
if (ncclNIbDevs == -1) {
|
|
|
|
|
pthread_mutex_lock(&ncclIbLock);
|
|
|
|
|
wrap_ibv_fork_init();
|
|
|
|
|
if (ncclNIbDevs == -1) {
|
|
|
|
|
ncclNIbDevs = 0;
|
|
|
|
|
if (findInterfaces(ncclIbIfName, &ncclIbIfAddr, MAX_IF_NAME_SIZE, 1) != 1) {
|
|
|
|
|
WARN("NET/IB : No IP interface found.");
|
2018-11-13 10:37:20 -08:00
|
|
|
return ncclInternalError;
|
2018-09-24 16:06:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Detect IB cards
|
|
|
|
|
int nIbDevs;
|
|
|
|
|
struct ibv_device** devices;
|
|
|
|
|
|
|
|
|
|
// Check if user defined which IB device:port to use
|
|
|
|
|
char* userIbEnv = getenv("NCCL_IB_HCA");
|
2020-05-12 14:40:18 -07:00
|
|
|
if (userIbEnv != NULL && shownIbHcaEnv++ == 0) INFO(NCCL_NET|NCCL_ENV, "NCCL_IB_HCA set to %s", userIbEnv);
|
2018-09-24 16:06:59 -07:00
|
|
|
struct netIf userIfs[MAX_IB_DEVS];
|
|
|
|
|
bool searchNot = userIbEnv && userIbEnv[0] == '^';
|
2019-11-19 14:57:39 -08:00
|
|
|
if (searchNot) userIbEnv++;
|
2019-07-10 06:45:41 +09:00
|
|
|
bool searchExact = userIbEnv && userIbEnv[0] == '=';
|
2019-11-19 14:57:39 -08:00
|
|
|
if (searchExact) userIbEnv++;
|
2018-09-24 16:06:59 -07:00
|
|
|
int nUserIfs = parseStringList(userIbEnv, userIfs, MAX_IB_DEVS);
|
|
|
|
|
|
2018-11-13 10:37:20 -08:00
|
|
|
if (ncclSuccess != wrap_ibv_get_device_list(&devices, &nIbDevs)) return ncclInternalError;
|
2018-09-24 16:06:59 -07:00
|
|
|
|
2019-07-16 08:41:56 -07:00
|
|
|
for (int d=0; d<nIbDevs && ncclNIbDevs<MAX_IB_DEVS; d++) {
|
2018-09-24 16:06:59 -07:00
|
|
|
struct ibv_context * context;
|
2018-12-13 15:56:12 -08:00
|
|
|
if (ncclSuccess != wrap_ibv_open_device(&context, devices[d]) || context == NULL) {
|
2018-09-24 16:06:59 -07:00
|
|
|
WARN("NET/IB : Unable to open device %s", devices[d]->name);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2019-07-16 08:41:56 -07:00
|
|
|
int nPorts = 0;
|
2018-12-13 15:56:12 -08:00
|
|
|
struct ibv_device_attr devAttr;
|
2019-03-14 19:39:20 -07:00
|
|
|
memset(&devAttr, 0, sizeof(devAttr));
|
2018-12-13 15:56:12 -08:00
|
|
|
if (ncclSuccess != wrap_ibv_query_device(context, &devAttr)) {
|
|
|
|
|
WARN("NET/IB : Unable to query device %s", devices[d]->name);
|
|
|
|
|
if (ncclSuccess != wrap_ibv_close_device(context)) { return ncclInternalError; }
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (int port = 1; port <= devAttr.phys_port_cnt; port++) {
|
|
|
|
|
struct ibv_port_attr portAttr;
|
|
|
|
|
if (ncclSuccess != wrap_ibv_query_port(context, port, &portAttr)) {
|
|
|
|
|
WARN("NET/IB : Unable to query port %d", port);
|
2018-09-24 16:06:59 -07:00
|
|
|
continue;
|
|
|
|
|
}
|
2018-12-13 15:56:12 -08:00
|
|
|
if (portAttr.state != IBV_PORT_ACTIVE) continue;
|
|
|
|
|
if (portAttr.link_layer != IBV_LINK_LAYER_INFINIBAND
|
|
|
|
|
&& portAttr.link_layer != IBV_LINK_LAYER_ETHERNET) continue;
|
2018-09-24 16:06:59 -07:00
|
|
|
|
2018-12-13 15:56:12 -08:00
|
|
|
// check against user specified HCAs/ports
|
2019-07-10 06:45:41 +09:00
|
|
|
if (! (matchIfList(devices[d]->name, port, userIfs, nUserIfs, searchExact) ^ searchNot)) {
|
2018-12-13 15:56:12 -08:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
TRACE(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;
|
2020-01-16 16:02:42 -08:00
|
|
|
ncclIbDevs[ncclNIbDevs].guid = devAttr.sys_image_guid;
|
2018-12-13 15:56:12 -08:00
|
|
|
ncclIbDevs[ncclNIbDevs].port = port;
|
|
|
|
|
ncclIbDevs[ncclNIbDevs].link = portAttr.link_layer;
|
2020-01-16 16:02:42 -08:00
|
|
|
ncclIbDevs[ncclNIbDevs].speed = ncclIbSpeed(portAttr.active_speed) * ncclIbWidth(portAttr.active_width);
|
2018-12-13 15:56:12 -08:00
|
|
|
ncclIbDevs[ncclNIbDevs].context = context;
|
|
|
|
|
strncpy(ncclIbDevs[ncclNIbDevs].devName, devices[d]->name, MAXNAMESIZE);
|
2020-01-16 16:02:42 -08:00
|
|
|
NCCLCHECK(ncclIbGetPciPath(ncclIbDevs[ncclNIbDevs].devName, &ncclIbDevs[ncclNIbDevs].pciPath, &ncclIbDevs[ncclNIbDevs].realPort));
|
|
|
|
|
ncclIbDevs[ncclNIbDevs].maxQp = devAttr.max_qp;
|
2018-12-13 15:56:12 -08:00
|
|
|
ncclNIbDevs++;
|
2019-07-16 08:41:56 -07:00
|
|
|
nPorts++;
|
2018-12-13 15:56:12 -08:00
|
|
|
pthread_create(&ncclIbAsyncThread, NULL, ncclIbAsyncThreadMain, context);
|
2018-09-24 16:06:59 -07:00
|
|
|
}
|
2019-07-16 08:41:56 -07:00
|
|
|
if (nPorts == 0 && ncclSuccess != wrap_ibv_close_device(context)) { return ncclInternalError; }
|
2018-09-24 16:06:59 -07:00
|
|
|
}
|
2018-11-13 10:37:20 -08:00
|
|
|
if (nIbDevs && (ncclSuccess != wrap_ibv_free_device_list(devices))) { return ncclInternalError; };
|
2018-09-24 16:06:59 -07:00
|
|
|
}
|
2018-12-13 15:56:12 -08:00
|
|
|
if (ncclNIbDevs == 0) {
|
|
|
|
|
INFO(NCCL_INIT|NCCL_NET, "NET/IB : No device found.");
|
|
|
|
|
} else {
|
|
|
|
|
char line[1024];
|
|
|
|
|
line[0] = '\0';
|
|
|
|
|
for (int d=0; d<ncclNIbDevs; d++) {
|
|
|
|
|
snprintf(line+strlen(line), 1023-strlen(line), " [%d]%s:%d/%s", d, ncclIbDevs[d].devName,
|
|
|
|
|
ncclIbDevs[d].port, ncclIbDevs[d].link == IBV_LINK_LAYER_INFINIBAND ? "IB" : "RoCE");
|
|
|
|
|
}
|
|
|
|
|
line[1023] = '\0';
|
2020-09-04 14:35:05 -07:00
|
|
|
char addrline[SOCKET_NAME_MAXLEN+1];
|
2018-12-13 15:56:12 -08:00
|
|
|
INFO(NCCL_INIT|NCCL_NET, "NET/IB : Using%s ; OOB %s:%s", line, ncclIbIfName, socketToString(&ncclIbIfAddr.sa, addrline));
|
|
|
|
|
}
|
2018-09-24 16:06:59 -07:00
|
|
|
pthread_mutex_unlock(&ncclIbLock);
|
|
|
|
|
}
|
2018-11-13 10:37:20 -08:00
|
|
|
return ncclSuccess;
|
2018-09-24 16:06:59 -07:00
|
|
|
}
|
|
|
|
|
|
2018-11-13 10:37:20 -08:00
|
|
|
ncclResult_t ncclIbDevices(int* ndev) {
|
2018-09-24 16:06:59 -07:00
|
|
|
*ndev = ncclNIbDevs;
|
2018-11-13 10:37:20 -08:00
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-24 16:06:59 -07:00
|
|
|
// Detect whether GDR can work on a given NIC with the current CUDA device
|
|
|
|
|
// Returns :
|
|
|
|
|
// ncclSuccess : GDR works
|
|
|
|
|
// ncclSystemError : no module or module loaded but not supported by GPU
|
|
|
|
|
ncclResult_t ncclIbGdrSupport(int ibDev) {
|
|
|
|
|
static int moduleLoaded = -1;
|
|
|
|
|
if (moduleLoaded == -1) {
|
|
|
|
|
moduleLoaded = (access("/sys/kernel/mm/memory_peers/nv_mem/version", F_OK) == -1) ? 0 : 1;
|
|
|
|
|
}
|
|
|
|
|
if (moduleLoaded == 0) return ncclSystemError;
|
2019-11-19 14:57:39 -08:00
|
|
|
return ncclSuccess;
|
2018-09-24 16:06:59 -07:00
|
|
|
}
|
|
|
|
|
|
2020-01-16 16:02:42 -08:00
|
|
|
static ncclResult_t GetSocketAddr(union socketAddress* addr) {
|
|
|
|
|
memcpy(addr, &ncclIbIfAddr, sizeof(*addr));
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
2018-09-24 16:06:59 -07:00
|
|
|
|
2020-01-16 16:02:42 -08:00
|
|
|
ncclResult_t ncclIbGetProperties(int dev, ncclNetProperties_t* props) {
|
|
|
|
|
props->name = ncclIbDevs[dev].devName;
|
|
|
|
|
props->pciPath = ncclIbDevs[dev].pciPath;
|
|
|
|
|
props->guid = ncclIbDevs[dev].guid;
|
|
|
|
|
props->ptrSupport = NCCL_PTR_HOST;
|
2018-11-13 10:37:20 -08:00
|
|
|
if (ncclIbGdrSupport(dev) != ncclSuccess) {
|
2019-11-19 14:57:39 -08:00
|
|
|
INFO(NCCL_NET,"NET/IB : GPU Direct RDMA Disabled for HCA %d '%s' (no module)", dev, ncclIbDevs[dev].devName);
|
2020-01-16 16:02:42 -08:00
|
|
|
} else {
|
|
|
|
|
props->ptrSupport |= NCCL_PTR_CUDA;
|
2018-09-24 16:06:59 -07:00
|
|
|
}
|
2020-01-16 16:02:42 -08:00
|
|
|
props->speed = ncclIbDevs[dev].speed;
|
|
|
|
|
props->port = ncclIbDevs[dev].port + ncclIbDevs[dev].realPort;
|
|
|
|
|
props->maxComms = ncclIbDevs[dev].maxQp;
|
2018-09-24 16:06:59 -07:00
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-04 14:35:05 -07:00
|
|
|
#define MAX_REQUESTS NCCL_NET_MAX_REQUESTS
|
2018-09-24 16:06:59 -07:00
|
|
|
|
|
|
|
|
struct ncclIbQpInfo {
|
|
|
|
|
uint32_t lid;
|
|
|
|
|
uint8_t ib_port;
|
|
|
|
|
uint32_t qpn;
|
|
|
|
|
|
|
|
|
|
// For RoCE
|
|
|
|
|
uint64_t spn;
|
|
|
|
|
uint64_t iid;
|
|
|
|
|
enum ibv_mtu mtu;
|
|
|
|
|
|
|
|
|
|
// FIFO RDMA info
|
|
|
|
|
uint32_t fifoRkey;
|
|
|
|
|
uint64_t fifoAddr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ncclIbHandle {
|
|
|
|
|
union socketAddress connectAddr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ncclIbRequest {
|
|
|
|
|
int used;
|
|
|
|
|
int type;
|
|
|
|
|
struct ncclIbVerbs* verbs;
|
2020-09-04 14:35:05 -07:00
|
|
|
int events;
|
2018-09-24 16:06:59 -07:00
|
|
|
int size;
|
2020-09-04 14:35:05 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ncclIbVerbs {
|
|
|
|
|
struct ibv_pd* pd;
|
|
|
|
|
struct ibv_cq* cq;
|
|
|
|
|
uint64_t pad[2];
|
|
|
|
|
struct ncclIbRequest reqs[MAX_REQUESTS];
|
2018-09-24 16:06:59 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ncclIbListenComm {
|
|
|
|
|
int dev;
|
|
|
|
|
int fd;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ncclIbSendFifo {
|
|
|
|
|
uint64_t addr;
|
|
|
|
|
int size;
|
|
|
|
|
uint32_t seq;
|
|
|
|
|
uint32_t rkey;
|
|
|
|
|
uint32_t ready;
|
2020-09-04 14:35:05 -07:00
|
|
|
uint64_t pad[1]; // Pad FIFO element size to be 32-bytes
|
2018-09-24 16:06:59 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ncclIbSendComm {
|
2018-12-13 15:56:12 -08:00
|
|
|
struct ncclIbVerbs verbs;
|
2018-09-24 16:06:59 -07:00
|
|
|
struct ncclIbSendFifo fifo[MAX_REQUESTS];
|
|
|
|
|
uint32_t fifoHead;
|
|
|
|
|
int fd;
|
|
|
|
|
int ready;
|
|
|
|
|
struct ibv_qp* qp;
|
|
|
|
|
struct ibv_mr* fifoMr;
|
|
|
|
|
};
|
2020-09-04 14:35:05 -07:00
|
|
|
// The SendFifo needs to be 32-byte aligned and each element needs
|
|
|
|
|
// to be a 32-byte multiple, so that an entry does not get split and
|
|
|
|
|
// written out of order when IB Relaxed Ordering is enabled
|
|
|
|
|
static_assert((offsetof(struct ncclIbSendComm, fifo) % 32) == 0, "ncclIbSendComm fifo must be 32-byte aligned");
|
|
|
|
|
static_assert((sizeof(struct ncclIbSendFifo) % 32) == 0, "ncclIbSendFifo element size must be 32-byte multiples");
|
2018-09-24 16:06:59 -07:00
|
|
|
|
|
|
|
|
struct ncclIbGpuFlush {
|
|
|
|
|
int enabled;
|
|
|
|
|
int hostMem;
|
|
|
|
|
struct ibv_mr* hostMr;
|
|
|
|
|
struct ibv_sge sge;
|
|
|
|
|
struct ibv_qp* qp;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ncclIbRemFifo {
|
|
|
|
|
struct ncclIbSendFifo elems[MAX_REQUESTS];
|
|
|
|
|
uint64_t addr;
|
|
|
|
|
uint32_t rkey;
|
|
|
|
|
uint32_t tail;
|
|
|
|
|
uint32_t flags;
|
|
|
|
|
struct ibv_mr* mr;
|
|
|
|
|
struct ibv_sge sge;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ncclIbRecvComm {
|
2018-12-13 15:56:12 -08:00
|
|
|
struct ncclIbVerbs verbs;
|
2018-09-24 16:06:59 -07:00
|
|
|
struct ncclIbRemFifo remFifo;
|
|
|
|
|
int fd;
|
|
|
|
|
int ready;
|
|
|
|
|
struct ibv_qp* qp;
|
|
|
|
|
struct ncclIbGpuFlush gpuFlush;
|
|
|
|
|
};
|
2020-09-04 14:35:05 -07:00
|
|
|
static_assert((offsetof(struct ncclIbRecvComm, remFifo) % 32) == 0, "ncclIbSendComm fifo must be 32-byte aligned");
|
2018-09-24 16:06:59 -07:00
|
|
|
|
|
|
|
|
ncclResult_t ncclIbInitVerbs(ibv_context* ctx, struct ncclIbVerbs* verbs) {
|
|
|
|
|
NCCLCHECK(wrap_ibv_alloc_pd(&verbs->pd, ctx));
|
2020-09-04 14:35:05 -07:00
|
|
|
// Recv requests can generate 2 completions (one for the post FIFO, one for the Recv).
|
|
|
|
|
NCCLCHECK(wrap_ibv_create_cq(&verbs->cq, ctx, 2*MAX_REQUESTS, NULL, NULL, 0));
|
2018-09-24 16:06:59 -07:00
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbDestroyVerbs(struct ncclIbVerbs* verbs) {
|
|
|
|
|
NCCLCHECK(wrap_ibv_destroy_cq(verbs->cq));
|
|
|
|
|
NCCLCHECK(wrap_ibv_dealloc_pd(verbs->pd));
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbCreateQp(uint8_t ib_port, struct ncclIbVerbs* verbs, int access_flags, struct ibv_qp** qp) {
|
|
|
|
|
struct ibv_qp_init_attr qpInitAttr;
|
|
|
|
|
memset(&qpInitAttr, 0, sizeof(struct ibv_qp_init_attr));
|
|
|
|
|
qpInitAttr.send_cq = verbs->cq;
|
|
|
|
|
qpInitAttr.recv_cq = verbs->cq;
|
|
|
|
|
qpInitAttr.qp_type = IBV_QPT_RC;
|
2020-09-04 14:35:05 -07:00
|
|
|
// We might send 2 messages per send (RDMA and RDMA_WITH_IMM)
|
2020-01-16 16:02:42 -08:00
|
|
|
qpInitAttr.cap.max_send_wr = 2*MAX_REQUESTS;
|
2018-09-24 16:06:59 -07:00
|
|
|
qpInitAttr.cap.max_recv_wr = MAX_REQUESTS;
|
|
|
|
|
qpInitAttr.cap.max_send_sge = 1;
|
|
|
|
|
qpInitAttr.cap.max_recv_sge = 1;
|
2020-09-04 14:35:05 -07:00
|
|
|
qpInitAttr.cap.max_inline_data = ncclParamIbUseInline() ? sizeof(struct ncclIbSendFifo) : 0;
|
2018-09-24 16:06:59 -07:00
|
|
|
NCCLCHECK(wrap_ibv_create_qp(qp, verbs->pd, &qpInitAttr));
|
|
|
|
|
struct ibv_qp_attr qpAttr;
|
|
|
|
|
memset(&qpAttr, 0, sizeof(struct ibv_qp_attr));
|
|
|
|
|
qpAttr.qp_state = IBV_QPS_INIT;
|
2020-09-04 14:35:05 -07:00
|
|
|
qpAttr.pkey_index = ncclParamIbPkey();
|
2018-09-24 16:06:59 -07:00
|
|
|
qpAttr.port_num = ib_port;
|
|
|
|
|
qpAttr.qp_access_flags = access_flags;
|
|
|
|
|
NCCLCHECK(wrap_ibv_modify_qp(*qp, &qpAttr, IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT | IBV_QP_ACCESS_FLAGS));
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbRtrQp(ibv_qp* qp, struct ncclIbQpInfo* info) {
|
|
|
|
|
struct ibv_qp_attr qpAttr;
|
|
|
|
|
memset(&qpAttr, 0, sizeof(struct ibv_qp_attr));
|
|
|
|
|
qpAttr.qp_state = IBV_QPS_RTR;
|
|
|
|
|
qpAttr.path_mtu = info->mtu;
|
|
|
|
|
qpAttr.dest_qp_num = info->qpn;
|
|
|
|
|
qpAttr.rq_psn = 0;
|
|
|
|
|
qpAttr.max_dest_rd_atomic = 1;
|
|
|
|
|
qpAttr.min_rnr_timer = 12;
|
|
|
|
|
if (info->lid == 0) {
|
|
|
|
|
qpAttr.ah_attr.is_global = 1;
|
|
|
|
|
qpAttr.ah_attr.grh.dgid.global.subnet_prefix = info->spn;
|
|
|
|
|
qpAttr.ah_attr.grh.dgid.global.interface_id = info->iid;
|
|
|
|
|
qpAttr.ah_attr.grh.flow_label = 0;
|
|
|
|
|
qpAttr.ah_attr.grh.sgid_index = ncclParamIbGidIndex();
|
|
|
|
|
qpAttr.ah_attr.grh.hop_limit = 255;
|
|
|
|
|
qpAttr.ah_attr.grh.traffic_class = ncclParamIbTc();
|
|
|
|
|
} else {
|
|
|
|
|
qpAttr.ah_attr.is_global = 0;
|
|
|
|
|
qpAttr.ah_attr.dlid = info->lid;
|
|
|
|
|
}
|
|
|
|
|
qpAttr.ah_attr.sl = ncclParamIbSl();
|
|
|
|
|
qpAttr.ah_attr.src_path_bits = 0;
|
|
|
|
|
qpAttr.ah_attr.port_num = info->ib_port;
|
|
|
|
|
NCCLCHECK(wrap_ibv_modify_qp(qp, &qpAttr, IBV_QP_STATE | IBV_QP_AV | IBV_QP_PATH_MTU | IBV_QP_DEST_QPN | IBV_QP_RQ_PSN | IBV_QP_MAX_DEST_RD_ATOMIC | IBV_QP_MIN_RNR_TIMER));
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbRtsQp(ibv_qp* qp) {
|
|
|
|
|
struct ibv_qp_attr qpAttr;
|
|
|
|
|
memset(&qpAttr, 0, sizeof(struct ibv_qp_attr));
|
|
|
|
|
qpAttr.qp_state = IBV_QPS_RTS;
|
|
|
|
|
qpAttr.timeout = ncclParamIbTimeout();
|
|
|
|
|
qpAttr.retry_cnt = ncclParamIbRetryCnt();
|
|
|
|
|
qpAttr.rnr_retry = 7;
|
|
|
|
|
qpAttr.sq_psn = 0;
|
|
|
|
|
qpAttr.max_rd_atomic = 1;
|
|
|
|
|
NCCLCHECK(wrap_ibv_modify_qp(qp, &qpAttr, IBV_QP_STATE | IBV_QP_TIMEOUT | IBV_QP_RETRY_CNT | IBV_QP_RNR_RETRY | IBV_QP_SQ_PSN | IBV_QP_MAX_QP_RD_ATOMIC));
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbListen(int dev, void* opaqueHandle, void** listenComm) {
|
|
|
|
|
struct ncclIbListenComm* comm;
|
|
|
|
|
NCCLCHECK(ncclCalloc(&comm, 1));
|
|
|
|
|
struct ncclIbHandle* handle = (struct ncclIbHandle*) opaqueHandle;
|
|
|
|
|
static_assert(sizeof(struct ncclIbHandle) < NCCL_NET_HANDLE_MAXSIZE, "ncclIbHandle size too large");
|
|
|
|
|
comm->dev = dev;
|
|
|
|
|
NCCLCHECK(GetSocketAddr(&(handle->connectAddr)));
|
|
|
|
|
NCCLCHECK(createListenSocket(&comm->fd, &handle->connectAddr));
|
|
|
|
|
*listenComm = comm;
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbConnect(int dev, void* opaqueHandle, void** sendComm) {
|
|
|
|
|
struct ncclIbSendComm* comm;
|
|
|
|
|
NCCLCHECK(ncclIbMalloc((void**)&comm, sizeof(struct ncclIbSendComm)));
|
|
|
|
|
|
|
|
|
|
struct ncclIbHandle* handle = (struct ncclIbHandle*) opaqueHandle;
|
|
|
|
|
NCCLCHECK(connectAddress(&comm->fd, &handle->connectAddr));
|
|
|
|
|
*sendComm = comm;
|
|
|
|
|
|
|
|
|
|
// IB Setup
|
|
|
|
|
ibv_context* ctx = ncclIbDevs[dev].context;
|
|
|
|
|
NCCLCHECK(ncclIbInitVerbs(ctx, &comm->verbs));
|
|
|
|
|
uint8_t ib_port = ncclIbDevs[dev].port;
|
|
|
|
|
NCCLCHECK(ncclIbCreateQp(ib_port, &comm->verbs, IBV_ACCESS_REMOTE_WRITE, &comm->qp));
|
|
|
|
|
|
|
|
|
|
// Send my QP Info to receiver through the socket. Hope this won't block.
|
|
|
|
|
struct ibv_port_attr portAttr;
|
|
|
|
|
NCCLCHECK(wrap_ibv_query_port(ctx, ib_port, &portAttr));
|
|
|
|
|
struct ncclIbQpInfo qpInfo;
|
|
|
|
|
qpInfo.ib_port = ib_port;
|
|
|
|
|
qpInfo.qpn = comm->qp->qp_num;
|
|
|
|
|
qpInfo.mtu = portAttr.active_mtu;
|
|
|
|
|
|
|
|
|
|
// Prepare my fifo
|
|
|
|
|
NCCLCHECK(wrap_ibv_reg_mr(&comm->fifoMr, comm->verbs.pd, comm->fifo, sizeof(struct ncclIbSendFifo)*MAX_REQUESTS, IBV_ACCESS_LOCAL_WRITE|IBV_ACCESS_REMOTE_WRITE|IBV_ACCESS_REMOTE_READ));
|
|
|
|
|
qpInfo.fifoRkey = comm->fifoMr->rkey;
|
|
|
|
|
qpInfo.fifoAddr = (uint64_t)comm->fifo;
|
|
|
|
|
|
|
|
|
|
// RoCE support
|
|
|
|
|
qpInfo.lid = portAttr.lid;
|
|
|
|
|
if (qpInfo.lid) { // IB
|
2018-12-13 15:56:12 -08:00
|
|
|
INFO(NCCL_NET,"NET/IB: Dev %d Port %d qpn %d mtu %d LID %d", dev, ib_port, qpInfo.qpn, qpInfo.mtu, qpInfo.lid);
|
2018-09-24 16:06:59 -07:00
|
|
|
} 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;
|
2018-12-13 15:56:12 -08:00
|
|
|
INFO(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);
|
2018-09-24 16:06:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NCCLCHECK(socketSend(comm->fd, &qpInfo, sizeof(qpInfo)));
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NCCL_PARAM(IbGdrFlushDisable, "GDR_FLUSH_DISABLE", 0);
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbAccept(void* listenComm, void** recvComm) {
|
|
|
|
|
struct ncclIbListenComm* lComm = (struct ncclIbListenComm*)listenComm;
|
|
|
|
|
struct ncclIbRecvComm* rComm;
|
|
|
|
|
NCCLCHECK(ncclIbMalloc((void**)&rComm, sizeof(struct ncclIbRecvComm)));
|
|
|
|
|
|
|
|
|
|
struct sockaddr_in sockaddr;
|
|
|
|
|
socklen_t socklen = sizeof(struct sockaddr_in);
|
|
|
|
|
SYSCHECKVAL(accept(lComm->fd, (struct sockaddr*)&sockaddr, &socklen), "accept", rComm->fd);
|
|
|
|
|
struct ncclIbQpInfo remQpInfo;
|
2020-09-04 14:35:05 -07:00
|
|
|
NCCLCHECK(socketRecv(rComm->fd, &remQpInfo, sizeof(remQpInfo)));
|
2018-09-24 16:06:59 -07:00
|
|
|
|
|
|
|
|
// IB setup
|
|
|
|
|
ibv_context* ctx = ncclIbDevs[lComm->dev].context;
|
|
|
|
|
uint8_t ib_port = ncclIbDevs[lComm->dev].port;
|
|
|
|
|
struct ibv_port_attr portAttr;
|
|
|
|
|
NCCLCHECK(wrap_ibv_query_port(ctx, ib_port, &portAttr));
|
|
|
|
|
union ibv_gid gid;
|
|
|
|
|
NCCLCHECK(wrap_ibv_query_gid(ctx, ib_port, ncclParamIbGidIndex(), &gid));
|
|
|
|
|
|
|
|
|
|
// QP Creation
|
|
|
|
|
NCCLCHECK(ncclIbInitVerbs(ctx, &rComm->verbs));
|
|
|
|
|
NCCLCHECK(ncclIbCreateQp(ib_port, &rComm->verbs, IBV_ACCESS_REMOTE_WRITE, &rComm->qp));
|
|
|
|
|
|
|
|
|
|
// Adjust the MTU
|
|
|
|
|
remQpInfo.mtu = (enum ibv_mtu)std::min(remQpInfo.mtu, portAttr.active_mtu);
|
|
|
|
|
|
|
|
|
|
// Setup QP
|
|
|
|
|
struct ibv_qp* qp = rComm->qp;
|
|
|
|
|
NCCLCHECK(ncclIbRtrQp(qp, &remQpInfo));
|
|
|
|
|
NCCLCHECK(ncclIbRtsQp(qp));
|
|
|
|
|
|
|
|
|
|
// Retain remote fifo info and prepare my RDMA ops
|
|
|
|
|
rComm->remFifo.rkey = remQpInfo.fifoRkey;
|
|
|
|
|
rComm->remFifo.addr = remQpInfo.fifoAddr;
|
|
|
|
|
NCCLCHECK(wrap_ibv_reg_mr(&rComm->remFifo.mr, rComm->verbs.pd, &rComm->remFifo.elems, sizeof(struct ncclIbSendFifo)*MAX_REQUESTS, IBV_ACCESS_REMOTE_WRITE|IBV_ACCESS_LOCAL_WRITE|IBV_ACCESS_REMOTE_READ));
|
|
|
|
|
rComm->remFifo.sge.length = sizeof(struct ncclIbSendFifo);
|
|
|
|
|
rComm->remFifo.sge.lkey = rComm->remFifo.mr->lkey;
|
2020-09-04 14:35:05 -07:00
|
|
|
if (ncclParamIbUseInline()) rComm->remFifo.flags = IBV_SEND_INLINE;
|
2018-09-24 16:06:59 -07:00
|
|
|
|
|
|
|
|
// Allocate Flush dummy buffer for GPU Direct RDMA
|
|
|
|
|
rComm->gpuFlush.enabled = (ncclIbGdrSupport(lComm->dev) == 0) && (ncclParamIbGdrFlushDisable() == 0) ? 1 : 0;
|
|
|
|
|
if (rComm->gpuFlush.enabled) {
|
|
|
|
|
NCCLCHECK(wrap_ibv_reg_mr(&rComm->gpuFlush.hostMr, rComm->verbs.pd, &rComm->gpuFlush.hostMem, sizeof(int), IBV_ACCESS_LOCAL_WRITE));
|
|
|
|
|
rComm->gpuFlush.sge.addr = (uint64_t)&rComm->gpuFlush.hostMem;
|
|
|
|
|
rComm->gpuFlush.sge.length = 1;
|
|
|
|
|
rComm->gpuFlush.sge.lkey = rComm->gpuFlush.hostMr->lkey;
|
|
|
|
|
NCCLCHECK(ncclIbCreateQp(ib_port, &rComm->verbs, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_READ, &rComm->gpuFlush.qp));
|
|
|
|
|
struct ncclIbQpInfo localQpInfo = {
|
|
|
|
|
.lid=portAttr.lid,
|
|
|
|
|
.ib_port=ib_port,
|
|
|
|
|
.qpn=rComm->gpuFlush.qp->qp_num,
|
|
|
|
|
.spn=gid.global.subnet_prefix,
|
|
|
|
|
.iid=gid.global.interface_id,
|
|
|
|
|
.mtu=portAttr.active_mtu
|
|
|
|
|
};
|
|
|
|
|
NCCLCHECK(ncclIbRtrQp(rComm->gpuFlush.qp, &localQpInfo));
|
|
|
|
|
NCCLCHECK(ncclIbRtsQp(rComm->gpuFlush.qp));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fill Handle
|
|
|
|
|
struct ncclIbQpInfo qpInfo = {
|
|
|
|
|
.lid=portAttr.lid,
|
|
|
|
|
.ib_port=ib_port,
|
|
|
|
|
.qpn=qp->qp_num,
|
|
|
|
|
.spn=gid.global.subnet_prefix,
|
|
|
|
|
.iid=gid.global.interface_id,
|
|
|
|
|
.mtu=remQpInfo.mtu
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
NCCLCHECK(socketSend(rComm->fd, &qpInfo, sizeof(qpInfo)));
|
|
|
|
|
*recvComm = rComm;
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-04 14:35:05 -07:00
|
|
|
ncclResult_t ncclIbGetRequest(struct ncclIbVerbs* verbs, struct ncclIbRequest** req) {
|
2018-09-24 16:06:59 -07:00
|
|
|
for (int i=0; i<MAX_REQUESTS; i++) {
|
2020-09-04 14:35:05 -07:00
|
|
|
struct ncclIbRequest* r = verbs->reqs+i;
|
2018-09-24 16:06:59 -07:00
|
|
|
if (r->used == 0) {
|
|
|
|
|
r->used = 1;
|
|
|
|
|
r->type = 0;
|
2020-09-04 14:35:05 -07:00
|
|
|
r->verbs = verbs;
|
|
|
|
|
r->events = 1;
|
2018-09-24 16:06:59 -07:00
|
|
|
r->size = -1;
|
|
|
|
|
*req = r;
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
WARN("NET/IB : unable to allocate requests");
|
|
|
|
|
*req = NULL;
|
|
|
|
|
return ncclInternalError;
|
|
|
|
|
}
|
2020-09-04 14:35:05 -07:00
|
|
|
ncclResult_t ncclIbFreeRequest(struct ncclIbRequest* r) {
|
|
|
|
|
r->used = 0;
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
2018-09-24 16:06:59 -07:00
|
|
|
|
|
|
|
|
ncclResult_t ncclSendCheck(struct ncclIbSendComm* comm) {
|
2018-11-19 17:43:50 -08:00
|
|
|
struct ncclIbQpInfo remQpInfo;
|
|
|
|
|
struct ibv_qp* qp = comm->qp;
|
|
|
|
|
|
|
|
|
|
// Do not block on this receive, return if not ready.
|
|
|
|
|
int bytes = 0;
|
|
|
|
|
NCCLCHECK(socketProgress(NCCL_SOCKET_RECV, comm->fd, &remQpInfo, sizeof(remQpInfo), &bytes));
|
|
|
|
|
if (bytes == 0) return ncclSuccess; // Try again later
|
|
|
|
|
NCCLCHECK(socketWait(NCCL_SOCKET_RECV, comm->fd, &remQpInfo, sizeof(remQpInfo), &bytes));
|
|
|
|
|
|
|
|
|
|
NCCLCHECK(ncclIbRtrQp(qp, &remQpInfo));
|
|
|
|
|
NCCLCHECK(ncclIbRtsQp(qp));
|
|
|
|
|
comm->ready = 1;
|
|
|
|
|
// Block until this is done. It *should* not block indefinitely.
|
|
|
|
|
NCCLCHECK(socketSend(comm->fd, &comm->ready, sizeof(int)));
|
|
|
|
|
|
2018-09-24 16:06:59 -07:00
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclRecvCheck(struct ncclIbRecvComm* comm) {
|
2018-11-19 17:43:50 -08:00
|
|
|
// Do not block on this receive, return if not ready.
|
|
|
|
|
int bytes = 0;
|
|
|
|
|
NCCLCHECK(socketProgress(NCCL_SOCKET_RECV, comm->fd, &comm->ready, sizeof(int), &bytes));
|
|
|
|
|
if (bytes == 0) return ncclSuccess; // Try again later
|
|
|
|
|
NCCLCHECK(socketWait(NCCL_SOCKET_RECV, comm->fd, &comm->ready, sizeof(int), &bytes));
|
2018-09-24 16:06:59 -07:00
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbTest(void* request, int* done, int* size);
|
|
|
|
|
|
|
|
|
|
#define REG_ALIGN (4096)
|
|
|
|
|
|
2018-12-13 15:56:12 -08:00
|
|
|
ncclResult_t ncclIbRegMr(void* comm, void* data, int size, int type, void** mhandle) {
|
2020-09-04 14:35:05 -07:00
|
|
|
static_assert(offsetof(struct ncclIbSendComm, verbs) == offsetof(struct ncclIbRecvComm, verbs), "Send and recv comms must have verbs at the same offset");
|
2018-12-13 15:56:12 -08:00
|
|
|
struct ncclIbVerbs* verbs = (struct ncclIbVerbs*)comm;
|
2018-09-24 16:06:59 -07:00
|
|
|
uint64_t addr = (uint64_t)data;
|
|
|
|
|
assert(size > 0);
|
|
|
|
|
|
|
|
|
|
// Deregister / register
|
|
|
|
|
uint64_t regAddr = addr & (~(REG_ALIGN-1));
|
|
|
|
|
uint64_t regSize = addr+size - regAddr;
|
|
|
|
|
regSize = ((regSize + REG_ALIGN-1) / REG_ALIGN ) * REG_ALIGN;
|
2018-12-13 15:56:12 -08:00
|
|
|
struct ibv_mr* mr;
|
|
|
|
|
NCCLCHECK(wrap_ibv_reg_mr(&mr, verbs->pd, (void*)regAddr, regSize, IBV_ACCESS_LOCAL_WRITE|IBV_ACCESS_REMOTE_WRITE|IBV_ACCESS_REMOTE_READ));
|
|
|
|
|
*mhandle = (void*)mr;
|
|
|
|
|
TRACE(NCCL_INIT,"regAddr %lx size %ld rkey %x", regAddr, regSize, mr->rkey);
|
2018-09-24 16:06:59 -07:00
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-13 15:56:12 -08:00
|
|
|
ncclResult_t ncclIbDeregMr(void* comm, void* mhandle) {
|
|
|
|
|
NCCLCHECK(wrap_ibv_dereg_mr((struct ibv_mr*)mhandle));
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbIsend(void* sendComm, void* data, int size, void* mhandle, void** request) {
|
2018-09-24 16:06:59 -07:00
|
|
|
struct ncclIbSendComm* comm = (struct ncclIbSendComm*)sendComm;
|
2018-11-19 17:43:50 -08:00
|
|
|
if (comm->ready == 0) NCCLCHECK(ncclSendCheck(comm));
|
|
|
|
|
if (comm->ready == 0) { *request = NULL; return ncclSuccess; }
|
|
|
|
|
|
2018-12-13 15:56:12 -08:00
|
|
|
struct ibv_mr* mr = (struct ibv_mr*)mhandle;
|
|
|
|
|
|
2018-11-19 17:43:50 -08:00
|
|
|
// Wait for the receiver to have posted the corresponding receive
|
|
|
|
|
volatile struct ncclIbSendFifo* slot = comm->fifo + (comm->fifoHead%MAX_REQUESTS);
|
|
|
|
|
volatile uint32_t * readyPtr = &slot->ready;
|
|
|
|
|
if (*readyPtr == 0) { *request = NULL; return ncclSuccess; }
|
2018-09-24 16:06:59 -07:00
|
|
|
|
|
|
|
|
struct ncclIbRequest* req;
|
2020-09-04 14:35:05 -07:00
|
|
|
NCCLCHECK(ncclIbGetRequest(&comm->verbs, &req));
|
2018-09-24 16:06:59 -07:00
|
|
|
req->size = size;
|
|
|
|
|
|
2021-04-12 16:00:11 -07:00
|
|
|
struct ibv_send_wr wr[2];
|
|
|
|
|
memset(&wr[0], 0, sizeof(wr[0]));
|
|
|
|
|
wr[0].wr_id = (uint64_t)req;
|
2018-09-24 16:06:59 -07:00
|
|
|
|
|
|
|
|
struct ibv_sge sge;
|
|
|
|
|
if (size == 0) {
|
2021-04-12 16:00:11 -07:00
|
|
|
wr[0].sg_list = NULL;
|
|
|
|
|
wr[0].num_sge = 0;
|
2018-09-24 16:06:59 -07:00
|
|
|
} else {
|
2018-12-13 15:56:12 -08:00
|
|
|
sge.addr=(uintptr_t)data; sge.length=(unsigned int)size; sge.lkey=mr->lkey;
|
2021-04-12 16:00:11 -07:00
|
|
|
wr[0].sg_list = &sge;
|
|
|
|
|
wr[0].num_sge = 1;
|
2018-09-24 16:06:59 -07:00
|
|
|
}
|
2020-09-04 14:35:05 -07:00
|
|
|
#if USE_RDMA_WRITE == 0
|
2021-04-12 16:00:11 -07:00
|
|
|
wr[0].opcode = IBV_WR_SEND;
|
|
|
|
|
wr[0].send_flags = IBV_SEND_SIGNALED;
|
2020-09-04 14:35:05 -07:00
|
|
|
#else
|
2018-09-24 16:06:59 -07:00
|
|
|
__sync_synchronize(); // order the readyPtr load against rkey load below
|
|
|
|
|
// Sanity checks to catch user collective call count/size mismatches
|
|
|
|
|
// plus any potential programming errors
|
2020-09-04 14:35:05 -07:00
|
|
|
if (size > slot->size || slot->size < 0 || slot->addr == 0 || slot->rkey == 0 || slot->seq != comm->fifoHead) {
|
2018-09-24 16:06:59 -07:00
|
|
|
WARN("NET/IB : collective mismatch error local size %d remote %d addr %lx rkey %x seq %x/%x",
|
|
|
|
|
size, slot->size, slot->addr, slot->rkey, slot->seq, comm->fifoHead);
|
|
|
|
|
return ncclInternalError;
|
|
|
|
|
}
|
2021-04-12 16:00:11 -07:00
|
|
|
wr[0].opcode = IBV_WR_RDMA_WRITE_WITH_IMM;
|
|
|
|
|
wr[0].send_flags = IBV_SEND_SIGNALED;
|
|
|
|
|
wr[0].wr.rdma.remote_addr = slot->addr;
|
|
|
|
|
wr[0].wr.rdma.rkey = slot->rkey;
|
|
|
|
|
wr[0].imm_data = size; // Send the message size via imm_data
|
2018-09-24 16:06:59 -07:00
|
|
|
__sync_synchronize();
|
|
|
|
|
#endif
|
|
|
|
|
// We must clear slot->ready, but reset other fields to aid
|
|
|
|
|
// debugging and sanity checks
|
|
|
|
|
slot->ready = 0;
|
|
|
|
|
slot->addr = 0ULL;
|
|
|
|
|
slot->rkey = slot->size = slot->seq = 0;
|
|
|
|
|
comm->fifoHead++;
|
|
|
|
|
|
2020-01-16 16:02:42 -08:00
|
|
|
|
|
|
|
|
#if USE_RDMA_WRITE
|
|
|
|
|
// When using adaptive routing, send the bulk of the data first as an
|
|
|
|
|
// RDMA_WRITE, then a 0-byte RDMA_WRITE_WITH_IMM to trigger a remote
|
|
|
|
|
// completion.
|
2021-04-12 16:00:11 -07:00
|
|
|
if (size > ncclParamIbArThreshold()) {
|
|
|
|
|
memset(&wr[1], 0, sizeof(wr[1]));
|
|
|
|
|
memcpy(&wr[1], &wr[0], sizeof(wr[0]));
|
|
|
|
|
wr[1].sg_list = NULL;
|
|
|
|
|
wr[1].num_sge = 0;
|
|
|
|
|
wr[0].next = &wr[1];
|
|
|
|
|
|
|
|
|
|
wr[0].opcode = IBV_WR_RDMA_WRITE;
|
|
|
|
|
wr[1].opcode = IBV_WR_RDMA_WRITE_WITH_IMM;
|
|
|
|
|
|
|
|
|
|
wr[0].send_flags = 0;
|
|
|
|
|
wr[1].send_flags = IBV_SEND_SIGNALED;
|
2020-01-16 16:02:42 -08:00
|
|
|
}
|
|
|
|
|
#endif
|
2021-04-12 16:00:11 -07:00
|
|
|
|
|
|
|
|
struct ibv_send_wr* bad_wr;
|
|
|
|
|
NCCLCHECK(wrap_ibv_post_send(comm->qp, wr, &bad_wr));
|
|
|
|
|
|
2018-09-24 16:06:59 -07:00
|
|
|
*request = req;
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-04 14:35:05 -07:00
|
|
|
ncclResult_t ncclIbPostFifo(struct ncclIbRecvComm* comm, uint32_t rkey, uint64_t addr, int size, struct ncclIbRequest* req) {
|
2018-09-24 16:06:59 -07:00
|
|
|
struct ibv_send_wr wr;
|
|
|
|
|
memset(&wr, 0, sizeof(wr));
|
|
|
|
|
|
2020-09-04 14:35:05 -07:00
|
|
|
int slot = comm->remFifo.tail%MAX_REQUESTS;
|
|
|
|
|
struct ncclIbSendFifo* localElem = comm->remFifo.elems + slot;
|
2018-09-24 16:06:59 -07:00
|
|
|
localElem->addr = addr;
|
|
|
|
|
localElem->rkey = rkey;
|
|
|
|
|
localElem->ready = 1;
|
|
|
|
|
localElem->size = size; // Sanity/Debugging
|
|
|
|
|
localElem->seq = comm->remFifo.tail; // Sanity/Debugging
|
2020-09-04 14:35:05 -07:00
|
|
|
wr.wr.rdma.remote_addr = comm->remFifo.addr + slot*sizeof(struct ncclIbSendFifo);
|
2018-09-24 16:06:59 -07:00
|
|
|
wr.wr.rdma.rkey = comm->remFifo.rkey;
|
|
|
|
|
comm->remFifo.sge.addr = (uint64_t)localElem;
|
|
|
|
|
wr.sg_list = &comm->remFifo.sge;
|
|
|
|
|
wr.num_sge = 1;
|
|
|
|
|
wr.opcode = IBV_WR_RDMA_WRITE;
|
2020-09-04 14:35:05 -07:00
|
|
|
wr.send_flags = comm->remFifo.flags; // IBV_SEND_INLINE
|
|
|
|
|
|
|
|
|
|
// We need to occasionally post a request with the IBV_SEND_SIGNALED flag, otherwise
|
|
|
|
|
// the send queue will never empty.
|
|
|
|
|
//
|
|
|
|
|
// From https://www.rdmamojo.com/2014/06/30/working-unsignaled-completions/
|
|
|
|
|
// "How to use Unsignaled Completion?" / "Gotchas and Pitfalls"
|
|
|
|
|
// All posted Send Requested, Signaled and Unsignaled, are considered outstanding until
|
|
|
|
|
// a Work Completion that they, or Send Requests that were posted after them, was polled
|
|
|
|
|
// from the Completion Queue associated with the Send Queue. This means if one works with
|
|
|
|
|
// a Queue Pair that was configured to work with Unsignaled Completions, he must make
|
|
|
|
|
// sure that occasionally (before the Send Queue is full with outstanding Send Requests)
|
|
|
|
|
// a Send Request that generate Work Completion will be posted.
|
|
|
|
|
//
|
|
|
|
|
// Not following this rule may lead to a case that the Send Queue is full with Send
|
|
|
|
|
// Requests that won't generate Work Completion:
|
|
|
|
|
//
|
|
|
|
|
// - The Send Queue is full, so no new Send Requests can be posted to it
|
|
|
|
|
// - The Send Queue can't be emptied, since no Work Completion can be generated anymore
|
|
|
|
|
// (the reason is that no Work Completion, that can generate Work Completion that
|
|
|
|
|
// polling it will empty the Send Queue, can be posted)
|
|
|
|
|
// - The status of all posted Send Request is considered unknown
|
|
|
|
|
//
|
|
|
|
|
if (slot == 0) {
|
|
|
|
|
wr.send_flags |= IBV_SEND_SIGNALED;
|
|
|
|
|
wr.wr_id = (uint64_t)req;
|
|
|
|
|
req->events++;
|
|
|
|
|
}
|
2018-09-24 16:06:59 -07:00
|
|
|
|
|
|
|
|
struct ibv_send_wr* bad_wr;
|
|
|
|
|
NCCLCHECK(wrap_ibv_post_send(comm->qp, &wr, &bad_wr));
|
|
|
|
|
comm->remFifo.tail++;
|
|
|
|
|
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-13 15:56:12 -08:00
|
|
|
ncclResult_t ncclIbIrecv(void* recvComm, void* data, int size, void* mhandle, void** request) {
|
2018-09-24 16:06:59 -07:00
|
|
|
struct ncclIbRecvComm* comm = (struct ncclIbRecvComm*)recvComm;
|
2018-11-19 17:43:50 -08:00
|
|
|
if (comm->ready == 0) NCCLCHECK(ncclRecvCheck(comm));
|
|
|
|
|
if (comm->ready == 0) { *request = NULL; return ncclSuccess; }
|
2018-09-24 16:06:59 -07:00
|
|
|
|
2018-12-13 15:56:12 -08:00
|
|
|
struct ibv_mr* mr = (struct ibv_mr*)mhandle;
|
|
|
|
|
|
2018-09-24 16:06:59 -07:00
|
|
|
struct ncclIbRequest* req;
|
2020-09-04 14:35:05 -07:00
|
|
|
NCCLCHECK(ncclIbGetRequest(&comm->verbs, &req));
|
2018-09-24 16:06:59 -07:00
|
|
|
req->size = size;
|
|
|
|
|
|
|
|
|
|
struct ibv_recv_wr wr;
|
|
|
|
|
memset(&wr, 0, sizeof(wr));
|
|
|
|
|
wr.wr_id = (uint64_t)req;
|
|
|
|
|
|
|
|
|
|
struct ibv_sge sge;
|
|
|
|
|
if (size == 0) {
|
|
|
|
|
wr.sg_list = NULL;
|
|
|
|
|
wr.num_sge = 0;
|
|
|
|
|
} else {
|
2018-12-13 15:56:12 -08:00
|
|
|
sge.addr=(uintptr_t)data; sge.length=(unsigned int)size; sge.lkey=mr->lkey;
|
2018-09-24 16:06:59 -07:00
|
|
|
wr.sg_list = &sge;
|
|
|
|
|
wr.num_sge = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ibv_recv_wr* bad_wr;
|
|
|
|
|
NCCLCHECK(wrap_ibv_post_recv(comm->qp, &wr, &bad_wr));
|
|
|
|
|
*request = req;
|
|
|
|
|
|
|
|
|
|
// Post to FIFO to notify sender
|
2020-09-04 14:35:05 -07:00
|
|
|
NCCLCHECK(ncclIbPostFifo(comm, mr->rkey, (uint64_t)data, size, req));
|
2018-09-24 16:06:59 -07:00
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-04 14:35:05 -07:00
|
|
|
ncclResult_t ncclIbIflush(void* recvComm, void* data, int size, void* mhandle, void** request) {
|
2018-09-24 16:06:59 -07:00
|
|
|
struct ncclIbRecvComm* comm = (struct ncclIbRecvComm*)recvComm;
|
|
|
|
|
if (comm->gpuFlush.enabled == 0 || size == 0) return ncclSuccess;
|
|
|
|
|
|
|
|
|
|
struct ncclIbRequest* req;
|
2020-09-04 14:35:05 -07:00
|
|
|
NCCLCHECK(ncclIbGetRequest(&comm->verbs, &req));
|
2018-12-13 15:56:12 -08:00
|
|
|
struct ibv_mr* mr = (struct ibv_mr*)mhandle;
|
2018-09-24 16:06:59 -07:00
|
|
|
|
|
|
|
|
struct ibv_send_wr wr;
|
|
|
|
|
memset(&wr, 0, sizeof(wr));
|
|
|
|
|
wr.wr_id = (uint64_t)req;
|
|
|
|
|
|
|
|
|
|
wr.wr.rdma.remote_addr = (uint64_t)data;
|
2018-12-13 15:56:12 -08:00
|
|
|
wr.wr.rdma.rkey = mr->rkey;
|
2018-09-24 16:06:59 -07:00
|
|
|
wr.sg_list = &comm->gpuFlush.sge;
|
|
|
|
|
wr.num_sge = 1;
|
|
|
|
|
wr.opcode = IBV_WR_RDMA_READ;
|
|
|
|
|
wr.send_flags = IBV_SEND_SIGNALED;
|
|
|
|
|
|
|
|
|
|
struct ibv_send_wr* bad_wr;
|
|
|
|
|
NCCLCHECK(wrap_ibv_post_send(comm->gpuFlush.qp, &wr, &bad_wr));
|
|
|
|
|
|
2020-09-04 14:35:05 -07:00
|
|
|
*request = req;
|
2018-09-24 16:06:59 -07:00
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbTest(void* request, int* done, int* size) {
|
|
|
|
|
struct ncclIbRequest *r = (struct ncclIbRequest*)request;
|
|
|
|
|
*done = 0;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
2020-09-04 14:35:05 -07:00
|
|
|
if (r->events == 0) {
|
2018-09-24 16:06:59 -07:00
|
|
|
*done = 1;
|
|
|
|
|
if (size) *size = r->size;
|
2020-09-04 14:35:05 -07:00
|
|
|
NCCLCHECK(ncclIbFreeRequest(r));
|
2018-09-24 16:06:59 -07:00
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int wrDone = 0;
|
2018-12-13 15:56:12 -08:00
|
|
|
struct ibv_wc wcs[4];
|
|
|
|
|
NCCLCHECK(wrap_ibv_poll_cq(r->verbs->cq, 4, wcs, &wrDone));
|
2018-09-24 16:06:59 -07:00
|
|
|
if (wrDone == 0) return ncclSuccess;
|
|
|
|
|
|
2018-12-13 15:56:12 -08:00
|
|
|
for (int w=0; w<wrDone; w++) {
|
|
|
|
|
struct ibv_wc *wc = wcs+w;
|
|
|
|
|
if (wc->status != IBV_WC_SUCCESS) {
|
|
|
|
|
WARN("NET/IB : Got completion with error %d, opcode %d, len %d, vendor err %d", wc->status, wc->opcode, wc->byte_len, wc->vendor_err);
|
|
|
|
|
return ncclSystemError;
|
|
|
|
|
}
|
2018-09-24 16:06:59 -07:00
|
|
|
|
2018-12-13 15:56:12 -08:00
|
|
|
struct ncclIbRequest* doneReq = (struct ncclIbRequest*)wc->wr_id;
|
|
|
|
|
if (doneReq) {
|
|
|
|
|
if (wc->opcode == IBV_WC_RECV) {
|
|
|
|
|
doneReq->size = wc->byte_len;
|
2018-09-24 16:06:59 -07:00
|
|
|
#if USE_RDMA_WRITE
|
2018-12-13 15:56:12 -08:00
|
|
|
} else if (wc->opcode == IBV_WC_RECV_RDMA_WITH_IMM) {
|
|
|
|
|
doneReq->size = wc->imm_data;
|
2018-09-24 16:06:59 -07:00
|
|
|
#endif
|
2018-12-13 15:56:12 -08:00
|
|
|
}
|
2020-09-04 14:35:05 -07:00
|
|
|
doneReq->events--;
|
2018-09-24 16:06:59 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbCloseSend(void* sendComm) {
|
|
|
|
|
struct ncclIbSendComm* comm = (struct ncclIbSendComm*)sendComm;
|
|
|
|
|
if (comm) {
|
|
|
|
|
close(comm->fd);
|
|
|
|
|
if (comm->qp != NULL) NCCLCHECK(wrap_ibv_destroy_qp(comm->qp));
|
|
|
|
|
if (comm->fifoMr != NULL) NCCLCHECK(wrap_ibv_dereg_mr(comm->fifoMr));
|
|
|
|
|
NCCLCHECK(ncclIbDestroyVerbs(&comm->verbs));
|
|
|
|
|
free(comm);
|
|
|
|
|
}
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbCloseRecv(void* recvComm) {
|
|
|
|
|
struct ncclIbRecvComm* comm = (struct ncclIbRecvComm*)recvComm;
|
|
|
|
|
if (comm) {
|
|
|
|
|
close(comm->fd);
|
|
|
|
|
if (comm->qp != NULL) NCCLCHECK(wrap_ibv_destroy_qp(comm->qp));
|
|
|
|
|
if (comm->gpuFlush.enabled) {
|
|
|
|
|
if (comm->gpuFlush.qp != NULL) NCCLCHECK(wrap_ibv_destroy_qp(comm->gpuFlush.qp));
|
|
|
|
|
if (comm->gpuFlush.hostMr != NULL) NCCLCHECK(wrap_ibv_dereg_mr(comm->gpuFlush.hostMr));
|
|
|
|
|
}
|
|
|
|
|
if (comm->remFifo.mr != NULL) NCCLCHECK(wrap_ibv_dereg_mr(comm->remFifo.mr));
|
|
|
|
|
NCCLCHECK(ncclIbDestroyVerbs(&comm->verbs));
|
|
|
|
|
free(comm);
|
|
|
|
|
}
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclResult_t ncclIbCloseListen(void* listenComm) {
|
|
|
|
|
struct ncclIbListenComm* comm = (struct ncclIbListenComm*)listenComm;
|
|
|
|
|
if (comm) {
|
|
|
|
|
close(comm->fd);
|
|
|
|
|
free(comm);
|
|
|
|
|
}
|
|
|
|
|
return ncclSuccess;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ncclNet_t ncclNetIb = {
|
|
|
|
|
"IB",
|
2018-11-13 10:37:20 -08:00
|
|
|
ncclIbInit,
|
2018-09-24 16:06:59 -07:00
|
|
|
ncclIbDevices,
|
2020-01-16 16:02:42 -08:00
|
|
|
ncclIbGetProperties,
|
2018-09-24 16:06:59 -07:00
|
|
|
ncclIbListen,
|
|
|
|
|
ncclIbConnect,
|
|
|
|
|
ncclIbAccept,
|
2018-12-13 15:56:12 -08:00
|
|
|
ncclIbRegMr,
|
|
|
|
|
ncclIbDeregMr,
|
2018-09-24 16:06:59 -07:00
|
|
|
ncclIbIsend,
|
|
|
|
|
ncclIbIrecv,
|
2020-09-04 14:35:05 -07:00
|
|
|
ncclIbIflush,
|
2018-09-24 16:06:59 -07:00
|
|
|
ncclIbTest,
|
|
|
|
|
ncclIbCloseSend,
|
|
|
|
|
ncclIbCloseRecv,
|
|
|
|
|
ncclIbCloseListen
|
|
|
|
|
};
|
|
|
|
|
|