Files
rocm-systems/src/transport/net_ib.cc
T

1342 строки
47 KiB
C++
Исходник Обычный вид История

2018-09-24 16:06:59 -07:00
/*************************************************************************
2022-01-07 06:39:55 -08:00
* Copyright (c) 2016-2022, 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>
2022-01-07 06:39:55 -08:00
#define ENABLE_TIMER 0
#include "timer.h"
2018-09-24 16:06:59 -07:00
#include "ibvwrap.h"
#define MAXNAMESIZE 64
2020-09-04 14:35:05 -07:00
static char ncclIbIfName[MAX_IF_NAME_SIZE+1];
2022-01-07 06:39:55 -08:00
static union ncclSocketAddress ncclIbIfAddr;
struct ncclIbMr {
uintptr_t addr;
int pages;
int refs;
ibv_mr *mr;
};
struct ncclIbMrCache {
struct ncclIbMr *slots;
int capacity, population;
};
2020-01-16 16:02:42 -08:00
2018-09-24 16:06:59 -07:00
static int ncclNIbDevs = -1;
2022-03-30 02:25:49 -07:00
struct alignas(64) ncclIbDev {
2022-01-07 06:39:55 -08:00
pthread_mutex_t lock;
2018-09-24 16:06:59 -07:00
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;
2022-01-07 06:39:55 -08:00
int pdRefs;
ibv_pd* pd;
2018-09-24 16:06:59 -07:00
char devName[MAXNAMESIZE];
2020-01-16 16:02:42 -08:00
char* pciPath;
int realPort;
int maxQp;
2022-01-07 06:39:55 -08:00
struct ncclIbMrCache mrCache;
2022-11-29 04:27:46 -08:00
int ar; // ADAPTIVE_ROUTING
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;
2022-01-07 06:39:55 -08:00
static int ncclIbRelaxedOrderingEnabled = 0;
2018-09-24 16:06:59 -07:00
NCCL_PARAM(IbGidIndex, "IB_GID_INDEX", 0);
2022-08-18 02:53:17 -07:00
NCCL_PARAM(IbTimeout, "IB_TIMEOUT", 18);
2018-09-24 16:06:59 -07:00
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);
2022-01-07 06:39:55 -08:00
NCCL_PARAM(IbPciRelaxedOrdering, "IB_PCI_RELAXED_ORDERING", 2);
2022-11-29 04:27:46 -08:00
NCCL_PARAM(IbAdaptiveRouting, "IB_ADAPTIVE_ROUTING", -2);
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';
2021-09-08 13:56:25 -07:00
// Also merge virtual functions (VF) into the same device
p[strlen(p)-3] = '0';
2020-01-16 16:02:42 -08:00
// 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;
}
2022-08-18 02:53:17 -07:00
static int ibvWidths[] = { 1, 4, 8, 12, 2 };
2023-02-02 12:52:47 -08:00
static int ibvSpeeds[] = {
2500, /* SDR */
5000, /* DDR */
10000, /* QDR */
10000, /* QDR */
14000, /* FDR */
25000, /* EDR */
50000, /* HDR */
100000 /* NDR */ };
2020-01-16 16:02:42 -08:00
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)];
}
2022-01-07 06:39:55 -08:00
// Determine whether RELAXED_ORDERING is enabled and possible
static int ncclIbRelaxedOrderingCapable(void) {
int roMode = ncclParamIbPciRelaxedOrdering();
ncclResult_t r = ncclInternalError;
if (roMode == 1 || roMode == 2) {
// Query IBVERBS_1.8 API - needed for IBV_ACCESS_RELAXED_ORDERING support
r = wrap_ibv_reg_mr_iova2(NULL, NULL, NULL, 0, 0, 0);
}
return r == ncclInternalError ? 0 : 1;
}
2018-11-13 10:37:20 -08:00
ncclResult_t ncclIbInit(ncclDebugLogger_t logFunction) {
2022-01-07 06:39:55 -08:00
if (ncclParamIbDisable()) return ncclInternalError;
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; }
2018-09-24 16:06:59 -07:00
if (ncclNIbDevs == -1) {
pthread_mutex_lock(&ncclIbLock);
wrap_ibv_fork_init();
if (ncclNIbDevs == -1) {
ncclNIbDevs = 0;
2022-01-07 06:39:55 -08:00
if (ncclFindInterfaces(ncclIbIfName, &ncclIbIfAddr, MAX_IF_NAME_SIZE, 1) != 1) {
2018-09-24 16:06:59 -07:00
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++;
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
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;
}
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
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");
2022-01-07 06:39:55 -08:00
pthread_mutex_init(&ncclIbDevs[ncclNIbDevs].lock, NULL);
2018-12-13 15:56:12 -08:00
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;
2022-01-07 06:39:55 -08:00
ncclIbDevs[ncclNIbDevs].pdRefs = 0;
ncclIbDevs[ncclNIbDevs].pd = NULL;
2018-12-13 15:56:12 -08:00
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;
2022-01-07 06:39:55 -08:00
ncclIbDevs[ncclNIbDevs].mrCache.capacity = 0;
ncclIbDevs[ncclNIbDevs].mrCache.population = 0;
ncclIbDevs[ncclNIbDevs].mrCache.slots = NULL;
2022-11-29 04:27:46 -08:00
// Enable ADAPTIVE_ROUTING by default on IB networks
// But allow it to be overloaded by an env parameter
ncclIbDevs[ncclNIbDevs].ar = (portAttr.link_layer == IBV_LINK_LAYER_INFINIBAND) ? 1 : 0;
if (ncclParamIbAdaptiveRouting() != -2) ncclIbDevs[ncclNIbDevs].ar = ncclParamIbAdaptiveRouting();
2022-01-07 06:39:55 -08:00
pthread_create(&ncclIbAsyncThread, NULL, ncclIbAsyncThreadMain, context);
ncclSetThreadName(ncclIbAsyncThread, "NCCL IbAsync %2d", ncclNIbDevs);
2022-05-03 01:30:26 -07:00
pthread_detach(ncclIbAsyncThread); // will not be pthread_join()'d
2018-12-13 15:56:12 -08:00
ncclNIbDevs++;
nPorts++;
2018-09-24 16:06:59 -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';
2022-01-07 06:39:55 -08:00
// Determine whether RELAXED_ORDERING is enabled and possible
ncclIbRelaxedOrderingEnabled = ncclIbRelaxedOrderingCapable();
2018-12-13 15:56:12 -08:00
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];
2022-01-07 06:39:55 -08:00
INFO(NCCL_INIT|NCCL_NET, "NET/IB : Using%s %s; OOB %s:%s", line, ncclIbRelaxedOrderingEnabled ? "[RO]" : "",
ncclIbIfName, ncclSocketToString(&ncclIbIfAddr, addrline));
2018-12-13 15:56:12 -08:00
}
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) {
2021-04-26 14:24:50 -07:00
// Check for the nv_peer_mem module being loaded
moduleLoaded = ((access("/sys/kernel/mm/memory_peers/nv_mem/version", F_OK) == -1) &&
// Also support the new nvidia-peermem module
(access("/sys/kernel/mm/memory_peers/nvidia-peermem/version", F_OK) == -1)) ? 0 : 1;
2018-09-24 16:06:59 -07:00
}
if (moduleLoaded == 0) return ncclSystemError;
2019-11-19 14:57:39 -08:00
return ncclSuccess;
2018-09-24 16:06:59 -07:00
}
2022-05-24 02:02:31 -07:00
// Detect whether DMA-BUF support is present in the kernel
// Returns :
// ncclSuccess : DMA-BUF support is available
// ncclSystemError : DMA-BUF is not supported by the kernel
ncclResult_t ncclIbDmaBufSupport(int dev) {
static int dmaBufSupported = -1;
if (dmaBufSupported == -1) {
ncclResult_t res;
struct ibv_pd* pd;
struct ibv_context* ctx;
ctx = ncclIbDevs[dev].context;
NCCLCHECKGOTO(wrap_ibv_alloc_pd(&pd, ctx), res, failure);
// Test kernel DMA-BUF support with a dummy call (fd=-1)
(void) wrap_direct_ibv_reg_dmabuf_mr(pd, 0ULL/*offset*/, 0ULL/*len*/, 0ULL/*iova*/, -1/*fd*/, 0/*flags*/);
2022-08-18 02:53:17 -07:00
// ibv_reg_dmabuf_mr() will fail with EOPNOTSUPP/EPROTONOSUPPORT if not supported (EBADF otherwise)
dmaBufSupported = (errno != EOPNOTSUPP && errno != EPROTONOSUPPORT) ? 1 : 0;
2022-05-24 02:02:31 -07:00
NCCLCHECKGOTO(wrap_ibv_dealloc_pd(pd), res, failure);
}
if (dmaBufSupported == 0) return ncclSystemError;
return ncclSuccess;
failure:
dmaBufSupported = 0;
return ncclSystemError;
}
2022-01-07 06:39:55 -08:00
#define NCCL_NET_IB_MAX_RECVS 8
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;
2022-05-24 02:02:31 -07:00
if (ncclIbGdrSupport(dev) == ncclSuccess) {
props->ptrSupport |= NCCL_PTR_CUDA; // GDR support via nv_peermem
}
if (ncclIbDmaBufSupport(dev) == ncclSuccess) {
props->ptrSupport |= NCCL_PTR_DMABUF; // GDR support via DMA-BUF
2018-09-24 16:06:59 -07:00
}
2020-01-16 16:02:42 -08:00
props->speed = ncclIbDevs[dev].speed;
2022-01-07 06:39:55 -08:00
props->latency = 0; // Not set
2020-01-16 16:02:42 -08:00
props->port = ncclIbDevs[dev].port + ncclIbDevs[dev].realPort;
props->maxComms = ncclIbDevs[dev].maxQp;
2022-01-07 06:39:55 -08:00
props->maxRecvs = NCCL_NET_IB_MAX_RECVS;
2018-09-24 16:06:59 -07:00
return ncclSuccess;
}
2022-01-07 06:39:55 -08:00
// We need to support NCCL_NET_MAX_REQUESTS for each concurrent receive
#define MAX_REQUESTS (NCCL_NET_MAX_REQUESTS*NCCL_NET_IB_MAX_RECVS)
static_assert(MAX_REQUESTS <= 256, "request id are encoded in wr_id and we need up to 8 requests ids per completion");
2018-09-24 16:06:59 -07:00
2021-07-08 14:12:04 -07:00
#define NCCL_IB_MAX_QPS 128
2018-09-24 16:06:59 -07:00
struct ncclIbQpInfo {
uint32_t lid;
uint8_t ib_port;
2022-01-07 06:39:55 -08:00
uint8_t link_layer;
2021-07-08 14:12:04 -07:00
uint32_t qpn[NCCL_IB_MAX_QPS];
2018-09-24 16:06:59 -07:00
// For RoCE
uint64_t spn;
uint64_t iid;
enum ibv_mtu mtu;
// FIFO RDMA info
uint32_t fifoRkey;
uint64_t fifoAddr;
};
2022-01-07 06:39:55 -08:00
enum ncclIbCommState {
ncclIbCommStateStart = 0,
ncclIbCommStateConnect = 1,
ncclIbCommStateAccept = 3,
ncclIbCommStateSend = 4,
ncclIbCommStateRecv = 5,
ncclIbCommStateConnected = 6,
};
struct ncclIbCommStage {
enum ncclIbCommState state;
int offset;
void* buffer;
void* comm;
};
2018-09-24 16:06:59 -07:00
struct ncclIbHandle {
2022-01-07 06:39:55 -08:00
union ncclSocketAddress connectAddr; // Filled by the target
2022-11-29 04:27:46 -08:00
uint64_t magic; // random number to help debugging
2022-01-07 06:39:55 -08:00
struct ncclIbCommStage stage; // Used by the other side when connecting
2018-09-24 16:06:59 -07:00
};
2022-01-07 06:39:55 -08:00
#define NCCL_NET_IB_REQ_UNUSED 0
#define NCCL_NET_IB_REQ_SEND 1
#define NCCL_NET_IB_REQ_RECV 2
#define NCCL_NET_IB_REQ_FLUSH 3
2018-09-24 16:06:59 -07:00
struct ncclIbRequest {
struct ncclIbVerbs* verbs;
2022-01-07 06:39:55 -08:00
int type;
2020-09-04 14:35:05 -07:00
int events;
2022-11-29 04:27:46 -08:00
struct ncclSocket* sock;
2022-01-07 06:39:55 -08:00
int nreqs;
union {
struct {
int size;
void* data;
uint32_t lkey;
int offset;
} send;
struct {
int sizes[NCCL_NET_IB_MAX_RECVS];
} recv;
};
2020-09-04 14:35:05 -07:00
};
struct ncclIbVerbs {
2022-01-07 06:39:55 -08:00
int dev;
struct ibv_pd* pd; // duplicate of ncclIbDevs[dev].pd
2020-09-04 14:35:05 -07:00
struct ibv_cq* cq;
2022-01-07 06:39:55 -08:00
uint64_t pad[1];
2020-09-04 14:35:05 -07:00
struct ncclIbRequest reqs[MAX_REQUESTS];
2018-09-24 16:06:59 -07:00
};
struct ncclIbListenComm {
int dev;
2022-01-07 06:39:55 -08:00
struct ncclSocket sock;
struct ncclIbCommStage stage;
2018-09-24 16:06:59 -07:00
};
struct ncclIbSendFifo {
uint64_t addr;
int size;
uint32_t rkey;
2022-01-07 06:39:55 -08:00
uint32_t nreqs;
uint32_t tag;
uint64_t idx;
2018-09-24 16:06:59 -07:00
};
struct ncclIbSendComm {
2018-12-13 15:56:12 -08:00
struct ncclIbVerbs verbs;
2022-01-07 06:39:55 -08:00
struct ncclIbSendFifo fifo[MAX_REQUESTS][NCCL_NET_IB_MAX_RECVS];
uint64_t fifoHead;
struct ncclIbRequest* fifoReqs[MAX_REQUESTS][NCCL_NET_IB_MAX_RECVS];
struct ibv_send_wr wrs[NCCL_NET_IB_MAX_RECVS+1];
struct ibv_sge sges[NCCL_NET_IB_MAX_RECVS];
struct ncclSocket sock;
2018-09-24 16:06:59 -07:00
int ready;
2021-07-08 14:12:04 -07:00
struct ibv_qp* qps[NCCL_IB_MAX_QPS];
int nqps;
2018-09-24 16:06:59 -07:00
struct ibv_mr* fifoMr;
2022-11-29 04:27:46 -08:00
int ar;
2018-09-24 16:06:59 -07:00
};
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 {
2022-01-07 06:39:55 -08:00
struct ncclIbSendFifo elems[MAX_REQUESTS][NCCL_NET_IB_MAX_RECVS];
uint64_t fifoTail;
2018-09-24 16:06:59 -07:00
uint64_t addr;
uint32_t rkey;
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;
2022-01-07 06:39:55 -08:00
struct ncclSocket sock;
2018-09-24 16:06:59 -07:00
int ready;
2021-07-08 14:12:04 -07:00
struct ibv_qp* qps[NCCL_IB_MAX_QPS];
int nqps;
2018-09-24 16:06:59 -07:00
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
2021-07-08 14:12:04 -07:00
NCCL_PARAM(IbQpsPerConn, "IB_QPS_PER_CONNECTION", 1);
2022-01-07 06:39:55 -08:00
ncclResult_t ncclIbInitVerbs(int dev, struct ibv_context* ctx, struct ncclIbVerbs* verbs) {
verbs->dev = dev;
pthread_mutex_lock(&ncclIbDevs[dev].lock);
if (0 == ncclIbDevs[dev].pdRefs++) {
ncclResult_t res;
NCCLCHECKGOTO(wrap_ibv_alloc_pd(&ncclIbDevs[dev].pd, ctx), res, failure);
if (0) {
failure:
pthread_mutex_unlock(&ncclIbDevs[dev].lock);
return res;
}
}
verbs->pd = ncclIbDevs[dev].pd;
pthread_mutex_unlock(&ncclIbDevs[dev].lock);
2020-09-04 14:35:05 -07:00
// Recv requests can generate 2 completions (one for the post FIFO, one for the Recv).
2021-07-08 14:12:04 -07:00
NCCLCHECK(wrap_ibv_create_cq(&verbs->cq, ctx, 2*MAX_REQUESTS*ncclParamIbQpsPerConn(), NULL, NULL, 0));
2018-09-24 16:06:59 -07:00
return ncclSuccess;
}
ncclResult_t ncclIbDestroyVerbs(struct ncclIbVerbs* verbs) {
2022-01-07 06:39:55 -08:00
ncclResult_t res;
2018-09-24 16:06:59 -07:00
NCCLCHECK(wrap_ibv_destroy_cq(verbs->cq));
2022-01-07 06:39:55 -08:00
pthread_mutex_lock(&ncclIbDevs[verbs->dev].lock);
if (0 == --ncclIbDevs[verbs->dev].pdRefs) {
NCCLCHECKGOTO(wrap_ibv_dealloc_pd(ncclIbDevs[verbs->dev].pd), res, returning);
}
res = ncclSuccess;
returning:
pthread_mutex_unlock(&ncclIbDevs[verbs->dev].lock);
return res;
2018-09-24 16:06:59 -07:00
}
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;
}
2022-01-07 06:39:55 -08:00
ncclResult_t ncclIbRtrQp(struct ibv_qp* qp, uint32_t qpn, struct ncclIbQpInfo* info) {
2018-09-24 16:06:59 -07:00
struct ibv_qp_attr qpAttr;
memset(&qpAttr, 0, sizeof(struct ibv_qp_attr));
qpAttr.qp_state = IBV_QPS_RTR;
qpAttr.path_mtu = info->mtu;
2021-07-08 14:12:04 -07:00
qpAttr.dest_qp_num = qpn;
2018-09-24 16:06:59 -07:00
qpAttr.rq_psn = 0;
qpAttr.max_dest_rd_atomic = 1;
qpAttr.min_rnr_timer = 12;
2022-01-07 06:39:55 -08:00
if (info->link_layer == IBV_LINK_LAYER_ETHERNET) {
2018-09-24 16:06:59 -07:00
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;
}
2022-01-07 06:39:55 -08:00
ncclResult_t ncclIbRtsQp(struct ibv_qp* qp) {
2018-09-24 16:06:59 -07:00
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");
2022-01-07 06:39:55 -08:00
memset(handle, 0, sizeof(struct ncclIbHandle));
2018-09-24 16:06:59 -07:00
comm->dev = dev;
2022-11-29 04:27:46 -08:00
handle->magic = NCCL_SOCKET_MAGIC;
NCCLCHECK(ncclSocketInit(&comm->sock, &ncclIbIfAddr, handle->magic, ncclSocketTypeNetIb, NULL, 1));
2022-01-07 06:39:55 -08:00
NCCLCHECK(ncclSocketListen(&comm->sock));
2022-11-29 04:27:46 -08:00
NCCLCHECK(ncclSocketGetAddr(&comm->sock, &handle->connectAddr));
2018-09-24 16:06:59 -07:00
*listenComm = comm;
return ncclSuccess;
}
ncclResult_t ncclIbConnect(int dev, void* opaqueHandle, void** sendComm) {
struct ncclIbHandle* handle = (struct ncclIbHandle*) opaqueHandle;
2022-01-07 06:39:55 -08:00
struct ncclIbCommStage* stage = &handle->stage;
struct ncclIbSendComm* comm = (struct ncclIbSendComm*)stage->comm;
2022-11-29 04:27:46 -08:00
int ready;
2022-01-07 06:39:55 -08:00
*sendComm = NULL;
if (stage->state == ncclIbCommStateConnect) goto ib_connect_check;
if (stage->state == ncclIbCommStateSend) goto ib_send;
if (stage->state != ncclIbCommStateStart) {
WARN("Error: trying to connect already connected sendComm");
return ncclInternalError;
}
2018-09-24 16:06:59 -07:00
2022-01-07 06:39:55 -08:00
NCCLCHECK(ncclIbMalloc((void**)&comm, sizeof(struct ncclIbSendComm)));
2022-11-29 04:27:46 -08:00
NCCLCHECK(ncclSocketInit(&comm->sock, &handle->connectAddr, handle->magic, ncclSocketTypeNetIb, NULL, 1));
2022-01-07 06:39:55 -08:00
stage->comm = comm;
stage->state = ncclIbCommStateConnect;
NCCLCHECK(ncclSocketConnect(&comm->sock));
ib_connect_check:
/* since ncclSocketConnect is async, we must check if connection is complete */
2022-11-29 04:27:46 -08:00
NCCLCHECK(ncclSocketReady(&comm->sock, &ready));
if (!ready) return ncclSuccess;
2021-07-08 14:12:04 -07:00
2018-09-24 16:06:59 -07:00
// IB Setup
2022-01-07 06:39:55 -08:00
struct ibv_context* ctx;
ctx = ncclIbDevs[dev].context;
NCCLCHECK(ncclIbInitVerbs(dev, ctx, &comm->verbs));
uint8_t ib_port;
ib_port = ncclIbDevs[dev].port;
2021-07-08 14:12:04 -07:00
comm->nqps = ncclParamIbQpsPerConn();
for (int q=0; q<comm->nqps; q++) {
NCCLCHECK(ncclIbCreateQp(ib_port, &comm->verbs, IBV_ACCESS_REMOTE_WRITE, comm->qps+q));
}
2022-11-29 04:27:46 -08:00
comm->ar = ncclIbDevs[dev].ar; // ADAPTIVE_ROUTING
2018-09-24 16:06:59 -07:00
// 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;
2021-07-08 14:12:04 -07:00
for (int q=0; q<comm->nqps; q++) qpInfo.qpn[q] = comm->qps[q]->qp_num;
2018-09-24 16:06:59 -07:00
qpInfo.mtu = portAttr.active_mtu;
// Prepare my fifo
2022-01-07 06:39:55 -08:00
NCCLCHECK(wrap_ibv_reg_mr(&comm->fifoMr, comm->verbs.pd, comm->fifo, sizeof(struct ncclIbSendFifo)*MAX_REQUESTS*NCCL_NET_IB_MAX_RECVS, IBV_ACCESS_LOCAL_WRITE|IBV_ACCESS_REMOTE_WRITE|IBV_ACCESS_REMOTE_READ));
2018-09-24 16:06:59 -07:00
qpInfo.fifoRkey = comm->fifoMr->rkey;
qpInfo.fifoAddr = (uint64_t)comm->fifo;
// RoCE support
qpInfo.lid = portAttr.lid;
2022-01-07 06:39:55 -08:00
qpInfo.link_layer = portAttr.link_layer;
if (qpInfo.link_layer == IBV_LINK_LAYER_INFINIBAND) { // IB
2021-07-08 14:12:04 -07:00
for (int q=0; q<comm->nqps; q++)
INFO(NCCL_NET,"NET/IB: Dev %d Port %d qpn %d mtu %d LID %d", dev, ib_port, qpInfo.qpn[q], 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;
2021-07-08 14:12:04 -07:00
for (int q=0; q<comm->nqps; q++)
INFO(NCCL_NET,"NET/IB: Dev %d Port %d qpn %d mtu %d GID %ld (%lX/%lX)", dev, ib_port, qpInfo.qpn[q], qpInfo.mtu, ncclParamIbGidIndex(), qpInfo.spn, qpInfo.iid);
2018-09-24 16:06:59 -07:00
}
2022-01-07 06:39:55 -08:00
stage->state = ncclIbCommStateSend;
stage->offset = 0;
NCCLCHECK(ncclIbMalloc((void**)&stage->buffer, sizeof(qpInfo)));
memcpy(stage->buffer, &qpInfo, sizeof(qpInfo));
ib_send:
NCCLCHECK(ncclSocketProgress(NCCL_SOCKET_SEND, &comm->sock, stage->buffer, sizeof(qpInfo), &stage->offset));
if (stage->offset != sizeof(qpInfo))
return ncclSuccess;
free(stage->buffer);
stage->state = ncclIbCommStateConnected;
*sendComm = comm;
2018-09-24 16:06:59 -07:00
return ncclSuccess;
}
NCCL_PARAM(IbGdrFlushDisable, "GDR_FLUSH_DISABLE", 0);
ncclResult_t ncclIbAccept(void* listenComm, void** recvComm) {
struct ncclIbListenComm* lComm = (struct ncclIbListenComm*)listenComm;
2022-01-07 06:39:55 -08:00
struct ncclIbCommStage* stage = &lComm->stage;
struct ncclIbRecvComm* rComm = (struct ncclIbRecvComm*)stage->comm;
2022-11-29 04:27:46 -08:00
int ready;
2022-01-07 06:39:55 -08:00
*recvComm = NULL;
2022-11-29 04:27:46 -08:00
if (stage->state == ncclIbCommStateAccept) goto ib_accept_check;
2022-01-07 06:39:55 -08:00
if (stage->state == ncclIbCommStateRecv) goto ib_recv;
if (stage->state == ncclIbCommStateSend) goto ib_send;
if (stage->state != ncclIbCommStateStart) {
WARN("Listencomm in unknown state %d\n", stage->state);
return ncclInternalError;
}
2018-09-24 16:06:59 -07:00
NCCLCHECK(ncclIbMalloc((void**)&rComm, sizeof(struct ncclIbRecvComm)));
2022-01-07 06:39:55 -08:00
stage->comm = rComm;
stage->state = ncclIbCommStateAccept;
2022-11-29 04:27:46 -08:00
NCCLCHECK(ncclSocketInit(&rComm->sock));
2022-01-07 06:39:55 -08:00
NCCLCHECK(ncclSocketAccept(&rComm->sock, &lComm->sock));
2022-11-29 04:27:46 -08:00
ib_accept_check:
NCCLCHECK(ncclSocketReady(&rComm->sock, &ready));
if (!ready) return ncclSuccess;
2018-09-24 16:06:59 -07:00
struct ncclIbQpInfo remQpInfo;
2022-01-07 06:39:55 -08:00
stage->state = ncclIbCommStateRecv;
stage->offset = 0;
NCCLCHECK(ncclIbMalloc((void**)&stage->buffer, sizeof(remQpInfo)));
ib_recv:
NCCLCHECK(ncclSocketProgress(NCCL_SOCKET_RECV, &rComm->sock, stage->buffer, sizeof(remQpInfo), &stage->offset));
if (stage->offset != sizeof(remQpInfo))
return ncclSuccess;
/* copy back the received info */
memcpy(&remQpInfo, stage->buffer, sizeof(struct ncclIbQpInfo));
2018-09-24 16:06:59 -07:00
// IB setup
2022-01-07 06:39:55 -08:00
struct ibv_context* ctx;
uint8_t ib_port;
ctx = ncclIbDevs[lComm->dev].context;
ib_port = ncclIbDevs[lComm->dev].port;
2018-09-24 16:06:59 -07:00
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
2022-01-07 06:39:55 -08:00
NCCLCHECK(ncclIbInitVerbs(lComm->dev, ctx, &rComm->verbs));
2021-07-08 14:12:04 -07:00
rComm->nqps = ncclParamIbQpsPerConn();
for (int q=0; q<rComm->nqps; q++) {
NCCLCHECK(ncclIbCreateQp(ib_port, &rComm->verbs, IBV_ACCESS_REMOTE_WRITE, rComm->qps+q));
}
2018-09-24 16:06:59 -07:00
// Adjust the MTU
remQpInfo.mtu = (enum ibv_mtu)std::min(remQpInfo.mtu, portAttr.active_mtu);
// Setup QP
2021-07-08 14:12:04 -07:00
for (int q=0; q<rComm->nqps; q++) {
struct ibv_qp* qp = rComm->qps[q];
NCCLCHECK(ncclIbRtrQp(qp, remQpInfo.qpn[q], &remQpInfo));
NCCLCHECK(ncclIbRtsQp(qp));
}
2018-09-24 16:06:59 -07:00
// Retain remote fifo info and prepare my RDMA ops
rComm->remFifo.rkey = remQpInfo.fifoRkey;
rComm->remFifo.addr = remQpInfo.fifoAddr;
2022-01-07 06:39:55 -08:00
NCCLCHECK(wrap_ibv_reg_mr(&rComm->remFifo.mr, rComm->verbs.pd, &rComm->remFifo.elems, sizeof(struct ncclIbSendFifo)*MAX_REQUESTS*NCCL_NET_IB_MAX_RECVS, IBV_ACCESS_REMOTE_WRITE|IBV_ACCESS_LOCAL_WRITE|IBV_ACCESS_REMOTE_READ));
2018-09-24 16:06:59 -07:00
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));
2021-07-08 14:12:04 -07:00
struct ncclIbQpInfo localQpInfo;
localQpInfo.lid=portAttr.lid;
2022-01-07 06:39:55 -08:00
localQpInfo.link_layer=portAttr.link_layer;
2021-07-08 14:12:04 -07:00
localQpInfo.ib_port=ib_port;
localQpInfo.spn=gid.global.subnet_prefix;
localQpInfo.iid=gid.global.interface_id;
localQpInfo.mtu=portAttr.active_mtu;
NCCLCHECK(ncclIbRtrQp(rComm->gpuFlush.qp, rComm->gpuFlush.qp->qp_num, &localQpInfo));
2018-09-24 16:06:59 -07:00
NCCLCHECK(ncclIbRtsQp(rComm->gpuFlush.qp));
}
// Fill Handle
2021-07-08 14:12:04 -07:00
struct ncclIbQpInfo qpInfo;
qpInfo.lid=portAttr.lid;
2022-01-07 06:39:55 -08:00
qpInfo.link_layer=portAttr.link_layer;
2021-07-08 14:12:04 -07:00
qpInfo.ib_port=ib_port;
for (int q=0; q<rComm->nqps; q++) qpInfo.qpn[q]=rComm->qps[q]->qp_num;
qpInfo.spn=gid.global.subnet_prefix;
qpInfo.iid=gid.global.interface_id;
qpInfo.mtu=remQpInfo.mtu;
2022-01-07 06:39:55 -08:00
stage->state = ncclIbCommStateSend;
stage->offset = 0;
if (stage->buffer) free(stage->buffer);
NCCLCHECK(ncclIbMalloc((void**)&stage->buffer, sizeof(struct ncclIbQpInfo)));
memcpy(stage->buffer, &qpInfo, sizeof(struct ncclIbQpInfo));
ib_send:
NCCLCHECK(ncclSocketProgress(NCCL_SOCKET_SEND, &rComm->sock, stage->buffer, sizeof(struct ncclIbQpInfo), &stage->offset));
if (stage->offset < sizeof(struct ncclIbQpInfo)) return ncclSuccess;
free(stage->buffer);
2018-09-24 16:06:59 -07:00
*recvComm = rComm;
2022-01-07 06:39:55 -08:00
/* reset lComm stage */
stage->state = ncclIbCommStateStart;
stage->offset = 0;
stage->comm = NULL;
stage->buffer = NULL;
2018-09-24 16:06:59 -07:00
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;
2022-01-07 06:39:55 -08:00
if (r->type == NCCL_NET_IB_REQ_UNUSED) {
2020-09-04 14:35:05 -07:00
r->verbs = verbs;
r->events = 1;
2022-11-29 04:27:46 -08:00
r->sock = NULL;
2018-09-24 16:06:59 -07:00
*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) {
2022-01-07 06:39:55 -08:00
r->type = NCCL_NET_IB_REQ_UNUSED;
2020-09-04 14:35:05 -07:00
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;
// Do not block on this receive, return if not ready.
int bytes = 0;
2022-01-07 06:39:55 -08:00
NCCLCHECK(ncclSocketProgress(NCCL_SOCKET_RECV, &comm->sock, &remQpInfo, sizeof(remQpInfo), &bytes));
2018-11-19 17:43:50 -08:00
if (bytes == 0) return ncclSuccess; // Try again later
2022-01-07 06:39:55 -08:00
NCCLCHECK(ncclSocketWait(NCCL_SOCKET_RECV, &comm->sock, &remQpInfo, sizeof(remQpInfo), &bytes));
2018-11-19 17:43:50 -08:00
2021-07-08 14:12:04 -07:00
for (int q=0; q<comm->nqps; q++) {
struct ibv_qp* qp = comm->qps[q];
NCCLCHECK(ncclIbRtrQp(qp, remQpInfo.qpn[q], &remQpInfo));
NCCLCHECK(ncclIbRtsQp(qp));
}
2018-11-19 17:43:50 -08:00
comm->ready = 1;
// Block until this is done. It *should* not block indefinitely.
2022-01-07 06:39:55 -08:00
NCCLCHECK(ncclSocketSend(&comm->sock, &comm->ready, sizeof(int)));
2018-11-19 17:43:50 -08:00
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;
2022-01-07 06:39:55 -08:00
NCCLCHECK(ncclSocketProgress(NCCL_SOCKET_RECV, &comm->sock, &comm->ready, sizeof(int), &bytes));
2018-11-19 17:43:50 -08:00
if (bytes == 0) return ncclSuccess; // Try again later
2022-01-07 06:39:55 -08:00
NCCLCHECK(ncclSocketWait(NCCL_SOCKET_RECV, &comm->sock, &comm->ready, sizeof(int), &bytes));
2018-09-24 16:06:59 -07:00
return ncclSuccess;
}
ncclResult_t ncclIbTest(void* request, int* done, int* size);
2022-05-24 02:02:31 -07:00
/* DMA-BUF support */
ncclResult_t ncclIbRegMrDmaBuf(void* comm, void* data, size_t size, int type, uint64_t offset, int fd, 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-09-24 16:06:59 -07:00
assert(size > 0);
2022-01-07 06:39:55 -08:00
static __thread uintptr_t pageSize = 0;
if (pageSize == 0) pageSize = sysconf(_SC_PAGESIZE);
struct ncclIbVerbs* verbs = (struct ncclIbVerbs*)comm;
struct ncclIbMrCache* cache = &ncclIbDevs[verbs->dev].mrCache;
uintptr_t addr = (uintptr_t)data & -pageSize;
2022-05-24 02:02:31 -07:00
size_t pages = ((uintptr_t)data + size - addr + pageSize-1)/pageSize;
2022-01-07 06:39:55 -08:00
ncclResult_t res;
pthread_mutex_lock(&ncclIbDevs[verbs->dev].lock);
for (int slot=0; /*true*/; slot++) {
if (slot == cache->population) { // didn't find in cache
if (cache->population == cache->capacity) { // must grow cache
cache->capacity = cache->capacity < 32 ? 32 : 2*cache->capacity;
NCCLCHECKGOTO(ncclRealloc(&cache->slots, cache->population, cache->capacity), res, returning);
}
// Deregister / register
struct ibv_mr* mr;
unsigned int flags = IBV_ACCESS_LOCAL_WRITE|IBV_ACCESS_REMOTE_WRITE|IBV_ACCESS_REMOTE_READ;
2022-05-24 02:02:31 -07:00
if (ncclIbRelaxedOrderingEnabled) flags |= IBV_ACCESS_RELAXED_ORDERING;
if (fd != -1) {
/* DMA-BUF support */
NCCLCHECKGOTO(wrap_ibv_reg_dmabuf_mr(&mr, verbs->pd, offset, pages*pageSize, addr, fd, flags), res, returning);
} else {
if (ncclIbRelaxedOrderingEnabled) {
// Use IBVERBS_1.8 API - needed for IBV_ACCESS_RELAXED_ORDERING support
NCCLCHECKGOTO(wrap_ibv_reg_mr_iova2(&mr, verbs->pd, (void*)addr, pages*pageSize, addr, flags), res, returning);
}
else {
NCCLCHECKGOTO(wrap_ibv_reg_mr(&mr, verbs->pd, (void*)addr, pages*pageSize, flags), res, returning);
}
2022-01-07 06:39:55 -08:00
}
2022-05-24 02:02:31 -07:00
TRACE(NCCL_INIT,"regAddr %llx size %lld rkey %x fd %d", (unsigned long long)addr, (long long)pages*pageSize, mr->rkey, fd);
2022-01-07 06:39:55 -08:00
cache->population += 1;
cache->slots[slot].addr = addr;
cache->slots[slot].pages = pages;
cache->slots[slot].refs = 1;
cache->slots[slot].mr = mr;
*mhandle = (void*)mr;
res = ncclSuccess;
goto returning;
}
else if (cache->slots[slot].addr == addr && cache->slots[slot].pages == pages) {
cache->slots[slot].refs += 1;
*mhandle = (void*)cache->slots[slot].mr;
res = ncclSuccess;
goto returning;
}
}
returning:
pthread_mutex_unlock(&ncclIbDevs[verbs->dev].lock);
return res;
2018-09-24 16:06:59 -07:00
}
2022-05-24 02:02:31 -07:00
ncclResult_t ncclIbRegMr(void* comm, void* data, int size, int type, void** mhandle) {
return ncclIbRegMrDmaBuf(comm, data, (size_t)size, type, 0ULL, -1, mhandle);
}
2018-12-13 15:56:12 -08:00
ncclResult_t ncclIbDeregMr(void* comm, void* mhandle) {
2022-01-07 06:39:55 -08:00
struct ncclIbVerbs* verbs = (struct ncclIbVerbs*)comm;
struct ncclIbMrCache* cache = &ncclIbDevs[verbs->dev].mrCache;
ncclResult_t res;
pthread_mutex_lock(&ncclIbDevs[verbs->dev].lock);
for (int i=0; i < cache->population; i++) {
if (mhandle == cache->slots[i].mr) {
if (0 == --cache->slots[i].refs) {
memmove(&cache->slots[i], &cache->slots[--cache->population], sizeof(struct ncclIbMr));
if (cache->population == 0) {
free(cache->slots);
cache->slots = NULL;
cache->capacity = 0;
}
NCCLCHECKGOTO(wrap_ibv_dereg_mr((struct ibv_mr*)mhandle), res, returning);
}
res = ncclSuccess;
goto returning;
}
}
WARN("NET/IB: could not find mr %p inside cache of %d entries", mhandle, cache->population);
res = ncclInternalError;
returning:
pthread_mutex_unlock(&ncclIbDevs[verbs->dev].lock);
return res;
}
ncclResult_t ncclIbMultiSend(struct ncclIbSendComm* comm, int slot) {
struct ncclIbRequest** reqs = comm->fifoReqs[slot];
volatile struct ncclIbSendFifo* slots = comm->fifo[slot];
int nreqs = slots[0].nreqs;
if (nreqs > NCCL_NET_IB_MAX_RECVS) return ncclInternalError;
uint64_t wr_id = 0ULL;
for (int r=0; r<nreqs; r++) {
struct ibv_send_wr* wr = comm->wrs+r;
memset(wr, 0, sizeof(struct ibv_send_wr));
struct ibv_sge* sge = comm->sges+r;
sge->addr=(uintptr_t)reqs[r]->send.data;
sge->lkey=reqs[r]->send.lkey;
wr->opcode = IBV_WR_RDMA_WRITE;
wr->send_flags = 0;
wr->wr.rdma.remote_addr = slots[r].addr;
wr->wr.rdma.rkey = slots[r].rkey;
wr->next = wr+1;
wr_id += (reqs[r] - comm->verbs.reqs) << (r*8);
}
// Write size as immediate data. In the case of multi-send, only write
// 0 or 1 as size to indicate whether there was data sent or received.
2022-05-24 02:02:31 -07:00
uint32_t immData = 0;
2022-01-07 06:39:55 -08:00
if (nreqs == 1) {
immData = reqs[0]->send.size;
} else {
2022-05-24 02:02:31 -07:00
if (nreqs > 32) {
WARN("Cannot store sizes of %d requests in a 32-bits field", nreqs);
return ncclInternalError;
}
2022-01-07 06:39:55 -08:00
for (int r=0; r<nreqs; r++) {
2022-05-24 02:02:31 -07:00
immData |= (reqs[r]->send.size ? 1 : 0) << r;
2022-01-07 06:39:55 -08:00
}
}
struct ibv_send_wr* lastWr = comm->wrs+nreqs-1;
2022-11-29 04:27:46 -08:00
if (nreqs > 1 || (comm->ar && reqs[0]->send.size > ncclParamIbArThreshold())) {
// When using ADAPTIVE_ROUTING, send the bulk of the data first as an
2022-01-07 06:39:55 -08:00
// RDMA_WRITE, then a 0-byte RDMA_WRITE_WITH_IMM to trigger a remote
// completion.
lastWr++;
memset(lastWr, 0, sizeof(struct ibv_send_wr));
}
lastWr->wr_id = wr_id;
lastWr->opcode = IBV_WR_RDMA_WRITE_WITH_IMM;
lastWr->imm_data = immData;
lastWr->next = NULL;
lastWr->send_flags = IBV_SEND_SIGNALED;
2022-03-30 02:25:49 -07:00
// Multi-QP: make sure IB writes are multiples of 128B so that LL and LL128 protocols still work
const int align = 128;
2022-01-07 06:39:55 -08:00
for (int q=0; q<comm->nqps; q++) {
for (int r=0; r<nreqs; r++) {
2022-03-30 02:25:49 -07:00
int chunkSize = DIVUP(DIVUP(reqs[r]->send.size, comm->nqps), align) * align;
2022-01-07 06:39:55 -08:00
int length = std::min(reqs[r]->send.size-reqs[r]->send.offset, chunkSize);
if (length <= 0) {
comm->wrs[r].sg_list = NULL;
comm->wrs[r].num_sge = 0;
} else {
comm->sges[r].length = length;
comm->wrs[r].sg_list = comm->sges+r;
comm->wrs[r].num_sge = 1;
}
}
struct ibv_send_wr* bad_wr;
NCCLCHECK(wrap_ibv_post_send(comm->qps[q], comm->wrs, &bad_wr));
for (int r=0; r<nreqs; r++) {
2022-03-30 02:25:49 -07:00
int chunkSize = DIVUP(DIVUP(reqs[r]->send.size, comm->nqps), align) * align;
2022-01-07 06:39:55 -08:00
reqs[r]->send.offset += chunkSize;
comm->sges[r].addr += chunkSize;
comm->wrs[r].wr.rdma.remote_addr += chunkSize;
}
}
2018-12-13 15:56:12 -08:00
return ncclSuccess;
}
2022-01-07 06:39:55 -08:00
ncclResult_t ncclIbIsend(void* sendComm, void* data, int size, int tag, 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
2022-01-07 06:39:55 -08:00
int nreqs = 0;
volatile struct ncclIbSendFifo* slots;
int slot = (comm->fifoHead)%MAX_REQUESTS;
struct ncclIbRequest** reqs = comm->fifoReqs[slot];
slots = comm->fifo[slot];
int idx = comm->fifoHead+1;
if (slots[0].idx != idx) { *request = NULL; return ncclSuccess; }
nreqs = slots[0].nreqs;
// Wait until all data has arrived
for (int r=1; r<nreqs; r++) while(slots[r].idx != idx);
__sync_synchronize(); // order the nreqsPtr load against tag/rkey/addr loads below
for (int r=0; r<nreqs; r++) {
if (reqs[r] != NULL || slots[r].tag != tag) continue;
// Sanity checks to catch user collective call count/size mismatches
2022-03-30 02:25:49 -07:00
if (size > slots[r].size) {
2022-11-29 04:27:46 -08:00
char line[SOCKET_NAME_MAXLEN + 1];
union ncclSocketAddress addr;
ncclSocketGetAddr(&comm->sock, &addr);
2022-03-30 02:25:49 -07:00
WARN("NET/IB : req %d/%d tag %x peer %s collective mismatch error, local size %d remote size %d",
2022-11-29 04:27:46 -08:00
r, nreqs, tag, ncclSocketToString(&addr, line), size, slots[r].size);
2022-03-30 02:25:49 -07:00
return ncclInvalidUsage;
} // plus any potential programming errors
else if (slots[r].size < 0 || slots[r].addr == 0 || slots[r].rkey == 0) {
2022-11-29 04:27:46 -08:00
char line[SOCKET_NAME_MAXLEN + 1];
union ncclSocketAddress addr;
ncclSocketGetAddr(&comm->sock, &addr);
WARN("NET/IB : req %d/%d tag %x peer %s posted incorrect receive info: size %d addr %lx rkey %x",
r, nreqs, tag, ncclSocketToString(&addr, line), slots[r].size, slots[r].addr, slots[r].rkey);
2022-01-07 06:39:55 -08:00
return ncclInternalError;
}
struct ncclIbRequest* req;
NCCLCHECK(ncclIbGetRequest(&comm->verbs, &req));
req->type = NCCL_NET_IB_REQ_SEND;
2022-11-29 04:27:46 -08:00
req->sock = &comm->sock;
2022-01-07 06:39:55 -08:00
req->verbs = &comm->verbs;
req->nreqs = nreqs;
req->send.size = size;
req->send.data = data;
req->send.lkey = mr->lkey;
req->send.offset = 0;
req->events = comm->nqps;
*request = reqs[r] = req;
// If this is a multi-recv, send only when all requests have matched.
for (int r=0; r<nreqs; r++) {
if (reqs[r] == NULL) return ncclSuccess;
}
2018-09-24 16:06:59 -07:00
2022-01-07 06:39:55 -08:00
TIME_START(0);
NCCLCHECK(ncclIbMultiSend(comm, slot));
2018-09-24 16:06:59 -07:00
2022-01-07 06:39:55 -08:00
// Clear slots[0]->nreqs, as well as other fields to help debugging and sanity checks
memset((void*)slots, 0, sizeof(struct ncclIbSendFifo));
memset(reqs, 0, NCCL_NET_IB_MAX_RECVS*sizeof(struct ncclIbRequest*));
comm->fifoHead++;
TIME_STOP(0);
return ncclSuccess;
2018-09-24 16:06:59 -07:00
}
2021-04-12 16:00:11 -07:00
2022-01-07 06:39:55 -08:00
*request = NULL;
2018-09-24 16:06:59 -07:00
return ncclSuccess;
}
2022-01-07 06:39:55 -08:00
ncclResult_t ncclIbPostFifo(struct ncclIbRecvComm* comm, int n, void** data, int* sizes, int* tags, void** mhandles, struct ncclIbRequest* req) {
2018-09-24 16:06:59 -07:00
struct ibv_send_wr wr;
memset(&wr, 0, sizeof(wr));
2022-01-07 06:39:55 -08:00
int slot = comm->remFifo.fifoTail%MAX_REQUESTS;
struct ncclIbSendFifo* localElem = comm->remFifo.elems[slot];
for (int i=0; i<n; i++) {
localElem[i].addr = (uint64_t)data[i];
struct ibv_mr* mr = (struct ibv_mr*)mhandles[i];
localElem[i].rkey = mr->rkey;
localElem[i].nreqs = n;
localElem[i].size = sizes[i]; // Sanity/Debugging
localElem[i].tag = tags[i];
localElem[i].idx = comm->remFifo.fifoTail+1;
}
wr.wr.rdma.remote_addr = comm->remFifo.addr + slot*NCCL_NET_IB_MAX_RECVS*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;
2022-01-07 06:39:55 -08:00
comm->remFifo.sge.length = n*sizeof(struct ncclIbSendFifo);
2018-09-24 16:06:59 -07:00
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;
2022-01-07 06:39:55 -08:00
wr.wr_id = req - comm->verbs.reqs;
2020-09-04 14:35:05 -07:00
req->events++;
}
2018-09-24 16:06:59 -07:00
struct ibv_send_wr* bad_wr;
2021-07-08 14:12:04 -07:00
NCCLCHECK(wrap_ibv_post_send(comm->qps[0], &wr, &bad_wr));
2022-01-07 06:39:55 -08:00
comm->remFifo.fifoTail++;
2018-09-24 16:06:59 -07:00
return ncclSuccess;
}
2022-01-07 06:39:55 -08:00
ncclResult_t ncclIbIrecv(void* recvComm, int n, void** data, int* sizes, int* tags, void** mhandles, 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; }
2022-01-07 06:39:55 -08:00
if (n > NCCL_NET_IB_MAX_RECVS) return ncclInternalError;
2018-12-13 15:56:12 -08:00
2018-09-24 16:06:59 -07:00
struct ncclIbRequest* req;
2020-09-04 14:35:05 -07:00
NCCLCHECK(ncclIbGetRequest(&comm->verbs, &req));
2022-01-07 06:39:55 -08:00
req->type = NCCL_NET_IB_REQ_RECV;
2022-11-29 04:27:46 -08:00
req->sock = &comm->sock;
2022-01-07 06:39:55 -08:00
req->nreqs = n;
for (int i=0; i<n; i++) req->recv.sizes[i] = 0;
2018-09-24 16:06:59 -07:00
struct ibv_recv_wr wr;
memset(&wr, 0, sizeof(wr));
2022-01-07 06:39:55 -08:00
wr.wr_id = req - comm->verbs.reqs;
2018-09-24 16:06:59 -07:00
2021-07-08 14:12:04 -07:00
wr.sg_list = NULL;
wr.num_sge = 0;
2022-01-07 06:39:55 -08:00
TIME_START(1);
2021-07-08 14:12:04 -07:00
for (int q=0; q<comm->nqps; q++) {
struct ibv_qp* qp = comm->qps[q];
struct ibv_recv_wr* bad_wr;
NCCLCHECK(wrap_ibv_post_recv(qp, &wr, &bad_wr));
2018-09-24 16:06:59 -07:00
}
2022-01-07 06:39:55 -08:00
TIME_STOP(1);
2021-07-08 14:12:04 -07:00
req->events = comm->nqps;
2018-09-24 16:06:59 -07:00
*request = req;
// Post to FIFO to notify sender
2022-01-07 06:39:55 -08:00
TIME_START(2);
NCCLCHECK(ncclIbPostFifo(comm, n, data, sizes, tags, mhandles, req));
TIME_STOP(2);
2018-09-24 16:06:59 -07:00
return ncclSuccess;
}
2022-01-07 06:39:55 -08:00
ncclResult_t ncclIbIflush(void* recvComm, int n, void** data, int* sizes, void** mhandles, void** request) {
2018-09-24 16:06:59 -07:00
struct ncclIbRecvComm* comm = (struct ncclIbRecvComm*)recvComm;
2022-01-07 06:39:55 -08:00
int last = -1;
for (int i=0; i<n; i++) if (sizes[i]) last = i;
if (comm->gpuFlush.enabled == 0 || last == -1) return ncclSuccess;
2018-09-24 16:06:59 -07:00
2022-01-07 06:39:55 -08:00
// Only flush once using the last non-zero receive
2018-09-24 16:06:59 -07:00
struct ncclIbRequest* req;
2020-09-04 14:35:05 -07:00
NCCLCHECK(ncclIbGetRequest(&comm->verbs, &req));
2022-01-07 06:39:55 -08:00
req->type = NCCL_NET_IB_REQ_FLUSH;
2022-11-29 04:27:46 -08:00
req->sock = &comm->sock;
2022-01-07 06:39:55 -08:00
struct ibv_mr* mr = (struct ibv_mr*)mhandles[last];
2018-09-24 16:06:59 -07:00
struct ibv_send_wr wr;
memset(&wr, 0, sizeof(wr));
2022-01-07 06:39:55 -08:00
wr.wr_id = req - comm->verbs.reqs;
2018-09-24 16:06:59 -07:00
2022-01-07 06:39:55 -08:00
wr.wr.rdma.remote_addr = (uint64_t)data[last];
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;
2022-01-07 06:39:55 -08:00
TIME_START(4);
2018-09-24 16:06:59 -07:00
struct ibv_send_wr* bad_wr;
NCCLCHECK(wrap_ibv_post_send(comm->gpuFlush.qp, &wr, &bad_wr));
2022-01-07 06:39:55 -08:00
TIME_STOP(4);
2018-09-24 16:06:59 -07:00
2020-09-04 14:35:05 -07:00
*request = req;
2018-09-24 16:06:59 -07:00
return ncclSuccess;
}
2022-01-07 06:39:55 -08:00
ncclResult_t ncclIbTest(void* request, int* done, int* sizes) {
2018-09-24 16:06:59 -07:00
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;
2022-01-07 06:39:55 -08:00
if (sizes && r->type == NCCL_NET_IB_REQ_RECV) {
for (int i=0; i<r->nreqs; i++) sizes[i] = r->recv.sizes[i];
}
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];
2022-01-07 06:39:55 -08:00
TIME_START(3);
2018-12-13 15:56:12 -08:00
NCCLCHECK(wrap_ibv_poll_cq(r->verbs->cq, 4, wcs, &wrDone));
2022-01-07 06:39:55 -08:00
if (wrDone == 0) { TIME_CANCEL(3); } else { TIME_STOP(3); }
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) {
2021-07-08 14:12:04 -07:00
char line[SOCKET_NAME_MAXLEN+1];
2022-11-29 04:27:46 -08:00
union ncclSocketAddress addr;
ncclSocketGetAddr(r->sock, &addr);
2021-07-08 14:12:04 -07:00
WARN("NET/IB : Got completion from peer %s with error %d, opcode %d, len %d, vendor err %d",
2022-11-29 04:27:46 -08:00
ncclSocketToString(&addr, line), wc->status, wc->opcode, wc->byte_len, wc->vendor_err);
2022-05-24 02:02:31 -07:00
return ncclRemoteError;
2018-12-13 15:56:12 -08:00
}
2018-09-24 16:06:59 -07:00
2022-01-07 06:39:55 -08:00
struct ncclIbRequest* req = r->verbs->reqs+(wc->wr_id & 0xff);
if (req->type == NCCL_NET_IB_REQ_SEND) {
for (int i=0; i<req->nreqs; i++) {
struct ncclIbRequest* sendReq = r->verbs->reqs+((wc->wr_id >> (i*8)) & 0xff);
if ((sendReq->events <= 0)) return ncclInternalError;
sendReq->events--;
}
} else {
if (req && wc->opcode == IBV_WC_RECV_RDMA_WITH_IMM) {
if (req->type != NCCL_NET_IB_REQ_RECV) return ncclInternalError;
if (req->nreqs > 1) {
// In the case of a multi recv, we only set sizes to 0 or 1.
for (int i=0; i<req->nreqs; i++) {
2022-05-24 02:02:31 -07:00
req->recv.sizes[i] = (wc->imm_data >> i) & 0x1;
2022-01-07 06:39:55 -08:00
}
} else {
req->recv.sizes[0] += wc->imm_data;
}
2018-12-13 15:56:12 -08:00
}
2022-01-07 06:39:55 -08:00
req->events--;
2018-09-24 16:06:59 -07:00
}
}
}
}
ncclResult_t ncclIbCloseSend(void* sendComm) {
struct ncclIbSendComm* comm = (struct ncclIbSendComm*)sendComm;
if (comm) {
2022-11-29 04:27:46 -08:00
NCCLCHECK(ncclSocketClose(&comm->sock));
2021-07-08 14:12:04 -07:00
for (int q=0; q<comm->nqps; q++)
if (comm->qps[q] != NULL) NCCLCHECK(wrap_ibv_destroy_qp(comm->qps[q]));
2018-09-24 16:06:59 -07:00
if (comm->fifoMr != NULL) NCCLCHECK(wrap_ibv_dereg_mr(comm->fifoMr));
NCCLCHECK(ncclIbDestroyVerbs(&comm->verbs));
free(comm);
}
2022-01-07 06:39:55 -08:00
TIME_PRINT("IB");
2018-09-24 16:06:59 -07:00
return ncclSuccess;
}
ncclResult_t ncclIbCloseRecv(void* recvComm) {
struct ncclIbRecvComm* comm = (struct ncclIbRecvComm*)recvComm;
if (comm) {
2022-11-29 04:27:46 -08:00
NCCLCHECK(ncclSocketClose(&comm->sock));
2021-07-08 14:12:04 -07:00
for (int q=0; q<comm->nqps; q++)
if (comm->qps[q] != NULL) NCCLCHECK(wrap_ibv_destroy_qp(comm->qps[q]));
2018-09-24 16:06:59 -07:00
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) {
2022-11-29 04:27:46 -08:00
NCCLCHECK(ncclSocketClose(&comm->sock));
2018-09-24 16:06:59 -07:00
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,
2022-05-24 02:02:31 -07:00
ncclIbRegMrDmaBuf,
2018-12-13 15:56:12 -08:00
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
};