19ab67d172
Optimize CUDA graph launch; avoid launching a CPU callback for intra-node operations. Simplify kernel common code to improve the latency of send/recv operations. Strengthen CUDA streams semantics. Change NET API to v6, to add dmabuf support. Add ncclGetLastError() function. Add ncclRemoteError code and use it for remote network errors. Support the use of a different NCCL_NET parameter per communicator. Add support for SHM and P2P transfers using cudaMemcpy.
555 lines
20 KiB
C++
555 lines
20 KiB
C++
/*************************************************************************
|
|
* Copyright (c) 2016-2022, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* See LICENSE.txt for license information
|
|
************************************************************************/
|
|
|
|
#include "socket.h"
|
|
#include "utils.h"
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
#include <ifaddrs.h>
|
|
#include <net/if.h>
|
|
|
|
/* Format a string representation of a (union ncclSocketAddress *) socket address using getnameinfo()
|
|
*
|
|
* Output: "IPv4/IPv6 address<port>"
|
|
*/
|
|
const char *ncclSocketToString(union ncclSocketAddress *addr, char *buf, const int numericHostForm /*= 1*/) {
|
|
if (buf == NULL || addr == NULL) return NULL;
|
|
struct sockaddr *saddr = &addr->sa;
|
|
if (saddr->sa_family != AF_INET && saddr->sa_family != AF_INET6) { buf[0]='\0'; return buf; }
|
|
char host[NI_MAXHOST], service[NI_MAXSERV];
|
|
/* NI_NUMERICHOST: If set, then the numeric form of the hostname is returned.
|
|
* (When not set, this will still happen in case the node's name cannot be determined.)
|
|
*/
|
|
int flag = NI_NUMERICSERV | (numericHostForm ? NI_NUMERICHOST : 0);
|
|
(void) getnameinfo(saddr, sizeof(union ncclSocketAddress), host, NI_MAXHOST, service, NI_MAXSERV, flag);
|
|
sprintf(buf, "%s<%s>", host, service);
|
|
return buf;
|
|
}
|
|
|
|
static uint16_t socketToPort(union ncclSocketAddress *addr) {
|
|
struct sockaddr *saddr = &addr->sa;
|
|
return ntohs(saddr->sa_family == AF_INET ? addr->sin.sin_port : addr->sin6.sin6_port);
|
|
}
|
|
|
|
/* Allow the user to force the IPv4/IPv6 interface selection */
|
|
static int envSocketFamily(void) {
|
|
int family = -1; // Family selection is not forced, will use first one found
|
|
char* env = getenv("NCCL_SOCKET_FAMILY");
|
|
if (env == NULL)
|
|
return family;
|
|
|
|
INFO(NCCL_ENV, "NCCL_SOCKET_FAMILY set by environment to %s", env);
|
|
|
|
if (strcmp(env, "AF_INET") == 0)
|
|
family = AF_INET; // IPv4
|
|
else if (strcmp(env, "AF_INET6") == 0)
|
|
family = AF_INET6; // IPv6
|
|
return family;
|
|
}
|
|
|
|
static int findInterfaces(const char* prefixList, char* names, union ncclSocketAddress *addrs, int sock_family, int maxIfNameSize, int maxIfs) {
|
|
#ifdef ENABLE_TRACE
|
|
char line[SOCKET_NAME_MAXLEN+1];
|
|
#endif
|
|
struct netIf userIfs[MAX_IFS];
|
|
bool searchNot = prefixList && prefixList[0] == '^';
|
|
if (searchNot) prefixList++;
|
|
bool searchExact = prefixList && prefixList[0] == '=';
|
|
if (searchExact) prefixList++;
|
|
int nUserIfs = parseStringList(prefixList, userIfs, MAX_IFS);
|
|
|
|
int found = 0;
|
|
struct ifaddrs *interfaces, *interface;
|
|
getifaddrs(&interfaces);
|
|
for (interface = interfaces; interface && found < maxIfs; interface = interface->ifa_next) {
|
|
if (interface->ifa_addr == NULL) continue;
|
|
|
|
/* We only support IPv4 & IPv6 */
|
|
int family = interface->ifa_addr->sa_family;
|
|
if (family != AF_INET && family != AF_INET6)
|
|
continue;
|
|
|
|
TRACE(NCCL_INIT|NCCL_NET,"Found interface %s:%s", interface->ifa_name, ncclSocketToString((union ncclSocketAddress *) interface->ifa_addr, line));
|
|
|
|
/* Allow the caller to force the socket family type */
|
|
if (sock_family != -1 && family != sock_family)
|
|
continue;
|
|
|
|
/* We also need to skip IPv6 loopback interfaces */
|
|
if (family == AF_INET6) {
|
|
struct sockaddr_in6* sa = (struct sockaddr_in6*)(interface->ifa_addr);
|
|
if (IN6_IS_ADDR_LOOPBACK(&sa->sin6_addr)) continue;
|
|
}
|
|
|
|
// check against user specified interfaces
|
|
if (!(matchIfList(interface->ifa_name, -1, userIfs, nUserIfs, searchExact) ^ searchNot)) {
|
|
continue;
|
|
}
|
|
|
|
// Check that this interface has not already been saved
|
|
// getifaddrs() normal order appears to be; IPv4, IPv6 Global, IPv6 Link
|
|
bool duplicate = false;
|
|
for (int i = 0; i < found; i++) {
|
|
if (strcmp(interface->ifa_name, names+i*maxIfNameSize) == 0) { duplicate = true; break; }
|
|
}
|
|
|
|
if (!duplicate) {
|
|
// Store the interface name
|
|
strncpy(names+found*maxIfNameSize, interface->ifa_name, maxIfNameSize);
|
|
// Store the IP address
|
|
int salen = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
|
|
memcpy(addrs+found, interface->ifa_addr, salen);
|
|
found++;
|
|
}
|
|
}
|
|
|
|
freeifaddrs(interfaces);
|
|
return found;
|
|
}
|
|
|
|
static bool matchSubnet(struct ifaddrs local_if, union ncclSocketAddress* remote) {
|
|
/* Check family first */
|
|
int family = local_if.ifa_addr->sa_family;
|
|
if (family != remote->sa.sa_family) {
|
|
return false;
|
|
}
|
|
|
|
if (family == AF_INET) {
|
|
struct sockaddr_in* local_addr = (struct sockaddr_in*)(local_if.ifa_addr);
|
|
struct sockaddr_in* mask = (struct sockaddr_in*)(local_if.ifa_netmask);
|
|
struct sockaddr_in& remote_addr = remote->sin;
|
|
struct in_addr local_subnet, remote_subnet;
|
|
local_subnet.s_addr = local_addr->sin_addr.s_addr & mask->sin_addr.s_addr;
|
|
remote_subnet.s_addr = remote_addr.sin_addr.s_addr & mask->sin_addr.s_addr;
|
|
return (local_subnet.s_addr ^ remote_subnet.s_addr) ? false : true;
|
|
} else if (family == AF_INET6) {
|
|
struct sockaddr_in6* local_addr = (struct sockaddr_in6*)(local_if.ifa_addr);
|
|
struct sockaddr_in6* mask = (struct sockaddr_in6*)(local_if.ifa_netmask);
|
|
struct sockaddr_in6& remote_addr = remote->sin6;
|
|
struct in6_addr& local_in6 = local_addr->sin6_addr;
|
|
struct in6_addr& mask_in6 = mask->sin6_addr;
|
|
struct in6_addr& remote_in6 = remote_addr.sin6_addr;
|
|
bool same = true;
|
|
int len = 16; //IPv6 address is 16 unsigned char
|
|
for (int c = 0; c < len; c++) { //Network byte order is big-endian
|
|
char c1 = local_in6.s6_addr[c] & mask_in6.s6_addr[c];
|
|
char c2 = remote_in6.s6_addr[c] & mask_in6.s6_addr[c];
|
|
if (c1 ^ c2) {
|
|
same = false;
|
|
break;
|
|
}
|
|
}
|
|
// At last, we need to compare scope id
|
|
// Two Link-type addresses can have the same subnet address even though they are not in the same scope
|
|
// For Global type, this field is 0, so a comparison wouldn't matter
|
|
same &= (local_addr->sin6_scope_id == remote_addr.sin6_scope_id);
|
|
return same;
|
|
} else {
|
|
WARN("Net : Unsupported address family type");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int ncclFindInterfaceMatchSubnet(char* ifNames, union ncclSocketAddress* localAddrs, union ncclSocketAddress* remoteAddr, int ifNameMaxSize, int maxIfs) {
|
|
#ifdef ENABLE_TRACE
|
|
char line[SOCKET_NAME_MAXLEN+1];
|
|
#endif
|
|
char line_a[SOCKET_NAME_MAXLEN+1];
|
|
int found = 0;
|
|
struct ifaddrs *interfaces, *interface;
|
|
getifaddrs(&interfaces);
|
|
for (interface = interfaces; interface && !found; interface = interface->ifa_next) {
|
|
if (interface->ifa_addr == NULL) continue;
|
|
|
|
/* We only support IPv4 & IPv6 */
|
|
int family = interface->ifa_addr->sa_family;
|
|
if (family != AF_INET && family != AF_INET6)
|
|
continue;
|
|
|
|
// check against user specified interfaces
|
|
if (!matchSubnet(*interface, remoteAddr)) {
|
|
continue;
|
|
}
|
|
|
|
// Store the local IP address
|
|
int salen = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
|
|
memcpy(localAddrs+found, interface->ifa_addr, salen);
|
|
|
|
// Store the interface name
|
|
strncpy(ifNames+found*ifNameMaxSize, interface->ifa_name, ifNameMaxSize);
|
|
|
|
TRACE(NCCL_INIT|NCCL_NET,"NET : Found interface %s:%s in the same subnet as remote address %s", interface->ifa_name, ncclSocketToString(localAddrs+found, line), ncclSocketToString(remoteAddr, line_a));
|
|
found++;
|
|
if (found == maxIfs) break;
|
|
}
|
|
|
|
if (found == 0) {
|
|
WARN("Net : No interface found in the same subnet as remote address %s", ncclSocketToString(remoteAddr, line_a));
|
|
}
|
|
freeifaddrs(interfaces);
|
|
return found;
|
|
}
|
|
|
|
ncclResult_t ncclGetSocketAddrFromString(union ncclSocketAddress* ua, const char* ip_port_pair) {
|
|
if (!(ip_port_pair && strlen(ip_port_pair) > 1)) {
|
|
WARN("Net : string is null");
|
|
return ncclInvalidArgument;
|
|
}
|
|
|
|
bool ipv6 = ip_port_pair[0] == '[';
|
|
/* Construct the sockaddress structure */
|
|
if (!ipv6) {
|
|
struct netIf ni;
|
|
// parse <ip_or_hostname>:<port> string, expect one pair
|
|
if (parseStringList(ip_port_pair, &ni, 1) != 1) {
|
|
WARN("Net : No valid <IPv4_or_hostname>:<port> pair found");
|
|
return ncclInvalidArgument;
|
|
}
|
|
|
|
struct addrinfo hints, *p;
|
|
int rv;
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
if ( (rv = getaddrinfo(ni.prefix, NULL, &hints, &p)) != 0) {
|
|
WARN("Net : error encountered when getting address info : %s", gai_strerror(rv));
|
|
return ncclInvalidArgument;
|
|
}
|
|
|
|
// use the first
|
|
if (p->ai_family == AF_INET) {
|
|
struct sockaddr_in& sin = ua->sin;
|
|
memcpy(&sin, p->ai_addr, sizeof(struct sockaddr_in));
|
|
sin.sin_family = AF_INET; // IPv4
|
|
//inet_pton(AF_INET, ni.prefix, &(sin.sin_addr)); // IP address
|
|
sin.sin_port = htons(ni.port); // port
|
|
} else if (p->ai_family == AF_INET6) {
|
|
struct sockaddr_in6& sin6 = ua->sin6;
|
|
memcpy(&sin6, p->ai_addr, sizeof(struct sockaddr_in6));
|
|
sin6.sin6_family = AF_INET6; // IPv6
|
|
sin6.sin6_port = htons(ni.port); // port
|
|
sin6.sin6_flowinfo = 0; // needed by IPv6, but possibly obsolete
|
|
sin6.sin6_scope_id = 0; // should be global scope, set to 0
|
|
} else {
|
|
WARN("Net : unsupported IP family");
|
|
return ncclInvalidArgument;
|
|
}
|
|
|
|
freeaddrinfo(p); // all done with this structure
|
|
|
|
} else {
|
|
int i, j = -1, len = strlen(ip_port_pair);
|
|
for (i = 1; i < len; i++) {
|
|
if (ip_port_pair[i] == '%') j = i;
|
|
if (ip_port_pair[i] == ']') break;
|
|
}
|
|
if (i == len) {
|
|
WARN("Net : No valid [IPv6]:port pair found");
|
|
return ncclInvalidArgument;
|
|
}
|
|
bool global_scope = (j == -1 ? true : false); // If no % found, global scope; otherwise, link scope
|
|
|
|
char ip_str[NI_MAXHOST], port_str[NI_MAXSERV], if_name[IFNAMSIZ];
|
|
memset(ip_str, '\0', sizeof(ip_str));
|
|
memset(port_str, '\0', sizeof(port_str));
|
|
memset(if_name, '\0', sizeof(if_name));
|
|
strncpy(ip_str, ip_port_pair+1, global_scope ? i-1 : j-1);
|
|
strncpy(port_str, ip_port_pair+i+2, len-i-1);
|
|
int port = atoi(port_str);
|
|
if (!global_scope) strncpy(if_name, ip_port_pair+j+1, i-j-1); // If not global scope, we need the intf name
|
|
|
|
struct sockaddr_in6& sin6 = ua->sin6;
|
|
sin6.sin6_family = AF_INET6; // IPv6
|
|
inet_pton(AF_INET6, ip_str, &(sin6.sin6_addr)); // IP address
|
|
sin6.sin6_port = htons(port); // port
|
|
sin6.sin6_flowinfo = 0; // needed by IPv6, but possibly obsolete
|
|
sin6.sin6_scope_id = global_scope ? 0 : if_nametoindex(if_name); // 0 if global scope; intf index if link scope
|
|
}
|
|
return ncclSuccess;
|
|
}
|
|
|
|
int ncclFindInterfaces(char* ifNames, union ncclSocketAddress *ifAddrs, int ifNameMaxSize, int maxIfs) {
|
|
static int shownIfName = 0;
|
|
int nIfs = 0;
|
|
// Allow user to force the INET socket family selection
|
|
int sock_family = envSocketFamily();
|
|
// User specified interface
|
|
char* env = getenv("NCCL_SOCKET_IFNAME");
|
|
if (env && strlen(env) > 1) {
|
|
INFO(NCCL_ENV, "NCCL_SOCKET_IFNAME set by environment to %s", env);
|
|
// Specified by user : find or fail
|
|
if (shownIfName++ == 0) INFO(NCCL_NET, "NCCL_SOCKET_IFNAME set to %s", env);
|
|
nIfs = findInterfaces(env, ifNames, ifAddrs, sock_family, ifNameMaxSize, maxIfs);
|
|
} else {
|
|
// Try to automatically pick the right one
|
|
// Start with IB
|
|
nIfs = findInterfaces("ib", ifNames, ifAddrs, sock_family, ifNameMaxSize, maxIfs);
|
|
// else see if we can get some hint from COMM ID
|
|
if (nIfs == 0) {
|
|
char* commId = getenv("NCCL_COMM_ID");
|
|
if (commId && strlen(commId) > 1) {
|
|
INFO(NCCL_ENV, "NCCL_COMM_ID set by environment to %s", commId);
|
|
// Try to find interface that is in the same subnet as the IP in comm id
|
|
union ncclSocketAddress idAddr;
|
|
ncclGetSocketAddrFromString(&idAddr, commId);
|
|
nIfs = ncclFindInterfaceMatchSubnet(ifNames, ifAddrs, &idAddr, ifNameMaxSize, maxIfs);
|
|
}
|
|
}
|
|
// Then look for anything else (but not docker or lo)
|
|
if (nIfs == 0) nIfs = findInterfaces("^docker,lo", ifNames, ifAddrs, sock_family, ifNameMaxSize, maxIfs);
|
|
// Finally look for docker, then lo.
|
|
if (nIfs == 0) nIfs = findInterfaces("docker", ifNames, ifAddrs, sock_family, ifNameMaxSize, maxIfs);
|
|
if (nIfs == 0) nIfs = findInterfaces("lo", ifNames, ifAddrs, sock_family, ifNameMaxSize, maxIfs);
|
|
}
|
|
return nIfs;
|
|
}
|
|
|
|
ncclResult_t ncclSocketListen(struct ncclSocket* sock) {
|
|
/* IPv4/IPv6 support */
|
|
int family = sock->addr.sa.sa_family;
|
|
int salen = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
|
|
int flags;
|
|
|
|
/* Create socket and bind it to a port */
|
|
int fd = socket(family, SOCK_STREAM, 0);
|
|
if (fd == -1) {
|
|
WARN("Net : Socket creation failed : %s", strerror(errno));
|
|
return ncclSystemError;
|
|
}
|
|
|
|
if (socketToPort(&sock->addr)) {
|
|
// Port is forced by env. Make sure we get the port.
|
|
int opt = 1;
|
|
#if defined(SO_REUSEPORT)
|
|
SYSCHECK(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)), "setsockopt");
|
|
#else
|
|
SYSCHECK(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)), "setsockopt");
|
|
#endif
|
|
}
|
|
|
|
if (sock->asyncFlag) {
|
|
EQCHECK(flags = fcntl(fd, F_GETFL), -1);
|
|
SYSCHECK(fcntl(fd, F_SETFL, flags | O_NONBLOCK), "fcntl");
|
|
}
|
|
|
|
// addr port should be 0 (Any port)
|
|
SYSCHECK(bind(fd, &sock->addr.sa, salen), "bind");
|
|
|
|
/* Get the assigned Port */
|
|
socklen_t size = salen;
|
|
SYSCHECK(getsockname(fd, &sock->addr.sa, &size), "getsockname");
|
|
|
|
#ifdef ENABLE_TRACE
|
|
char line[SOCKET_NAME_MAXLEN+1];
|
|
TRACE(NCCL_INIT|NCCL_NET,"Listening on socket %s", ncclSocketToString(&sock->addr, line));
|
|
#endif
|
|
|
|
/* Put the socket in listen mode
|
|
* NB: The backlog will be silently truncated to the value in /proc/sys/net/core/somaxconn
|
|
*/
|
|
SYSCHECK(listen(fd, 16384), "listen");
|
|
sock->fd = fd;
|
|
return ncclSuccess;
|
|
}
|
|
|
|
static ncclResult_t getFdState(int fd, enum ncclSocketState* state) {
|
|
struct pollfd pfd;
|
|
int timeout = 1, ret;
|
|
socklen_t rlen = sizeof(int);
|
|
|
|
memset(&pfd, 0, sizeof(struct pollfd));
|
|
pfd.fd = fd;
|
|
pfd.events = POLLOUT;
|
|
SYSCHECK(ret = poll(&pfd, 1, timeout), "poll");
|
|
if (ret == 0) {
|
|
ret = EINPROGRESS;
|
|
} else {
|
|
/* check socket status */
|
|
EQCHECK(ret == 1 && (pfd.revents & POLLOUT), 0);
|
|
SYSCHECK(getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&ret, &rlen), "getsockopt");
|
|
}
|
|
|
|
if (ret == EINPROGRESS || ret == ECONNREFUSED)
|
|
*state = ncclSocketConnecting;
|
|
else if (ret == 0)
|
|
*state = ncclSocketConnected;
|
|
else
|
|
*state = ncclSocketError;
|
|
return ncclSuccess;
|
|
}
|
|
|
|
ncclResult_t ncclGetSocketState(struct ncclSocket* sock, enum ncclSocketState* state) {
|
|
NCCLCHECK(getFdState(sock->fd, state));
|
|
sock->state = *state;
|
|
return ncclSuccess;
|
|
}
|
|
|
|
ncclResult_t ncclSocketConnect(struct ncclSocket* sock) {
|
|
char line[SOCKET_NAME_MAXLEN+1];
|
|
/* IPv4/IPv6 support */
|
|
int family = sock->addr.sa.sa_family;
|
|
if (family != AF_INET && family != AF_INET6) {
|
|
WARN("Net : connecting to address %s with family %d is neither AF_INET(%d) nor AF_INET6(%d)",
|
|
ncclSocketToString(&sock->addr, line), family, AF_INET, AF_INET6);
|
|
return ncclInternalError;
|
|
}
|
|
int salen = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
|
|
int flags;
|
|
|
|
/* Connect to a hostname / port */
|
|
int fd = socket(family, SOCK_STREAM, 0);
|
|
if (fd == -1) {
|
|
WARN("Net : Socket creation failed : %s", strerror(errno));
|
|
return ncclSystemError;
|
|
}
|
|
|
|
const int one = 1;
|
|
SYSCHECK(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(int)), "setsockopt");
|
|
|
|
/* support non-blocking socket; by default, the socket is non-blocking */
|
|
if (sock->asyncFlag) {
|
|
EQCHECK(flags = fcntl(fd, F_GETFL), -1);
|
|
SYSCHECK(fcntl(fd, F_SETFL, flags | O_NONBLOCK), "fcntl");
|
|
}
|
|
|
|
/* const int bufsize = 128*1024;
|
|
SYSCHECK(setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char*)&bufsize, sizeof(int)), "setsockopt");
|
|
SYSCHECK(setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, sizeof(int)), "setsockopt");*/
|
|
|
|
TRACE(NCCL_INIT|NCCL_NET,"Connecting to socket %s", ncclSocketToString(&sock->addr, line));
|
|
|
|
int ret;
|
|
int timedout_retries = 0;
|
|
int refused_retries = 0;
|
|
retry:
|
|
/* blocking/non-blocking connect() is determined by asyncFlag. */
|
|
ret = connect(fd, &sock->addr.sa, salen);
|
|
|
|
if (!sock->asyncFlag && (errno == EAGAIN || (errno == ECONNREFUSED && ++refused_retries < RETRY_REFUSED_TIMES) ||
|
|
(errno == ETIMEDOUT && ++timedout_retries < RETRY_TIMEDOUT_TIMES))) {
|
|
if (errno == ECONNREFUSED && refused_retries % 1000 == 0) INFO(NCCL_ALL, "Call to connect returned %s, retrying", strerror(errno));
|
|
usleep(SLEEP_INT);
|
|
goto retry;
|
|
}
|
|
|
|
/* If connect() fails with errno == EAGAIN/EINPROGRESS/ETIMEDOUT, we may want to try connect again.
|
|
* However, it can return EISCONN instead of success which indicates connection is built up in
|
|
* background already. No need to call connect() again. */
|
|
if (ret == 0 || ((errno == EINPROGRESS || errno == ECONNREFUSED) && sock->asyncFlag) || errno == EISCONN) {
|
|
sock->fd = fd;
|
|
return ncclSuccess;
|
|
}
|
|
|
|
WARN("Net : Connect to %s failed : %s", ncclSocketToString(&sock->addr, line), strerror(errno));
|
|
return ncclRemoteError;
|
|
}
|
|
|
|
ncclResult_t ncclSocketAccept(struct ncclSocket* sock, struct ncclSocket* listenSocket) {
|
|
socklen_t socklen = sizeof(union ncclSocketAddress);
|
|
int tmpFd = sock->fd = -1;
|
|
|
|
do {
|
|
if (listenSocket->abortFlag) NEQCHECK(*listenSocket->abortFlag, 0);
|
|
tmpFd = accept(listenSocket->fd, &sock->addr.sa, &socklen);
|
|
} while ((errno == EAGAIN || errno == EWOULDBLOCK) && tmpFd == -1 && !listenSocket->asyncFlag);
|
|
|
|
if (!listenSocket->asyncFlag) {
|
|
EQCHECK(tmpFd, -1);
|
|
} else if (tmpFd == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
|
|
return ncclSystemError;
|
|
}
|
|
|
|
sock->fd = tmpFd;
|
|
return ncclSuccess;
|
|
}
|
|
|
|
ncclResult_t ncclSocketInit(struct ncclSocket* sock, union ncclSocketAddress* addr, volatile uint32_t* abortFlag, int asyncFlag) {
|
|
if (sock == NULL)
|
|
return ncclSuccess;
|
|
|
|
sock->fd = -1;
|
|
if (addr) {
|
|
memcpy(&sock->addr, addr, sizeof(union ncclSocketAddress));
|
|
} else {
|
|
memset(&sock->addr, 0, sizeof(union ncclSocketAddress));
|
|
}
|
|
sock->abortFlag = abortFlag;
|
|
sock->asyncFlag = asyncFlag;
|
|
sock->state = ncclSocketStateNum;
|
|
return ncclSuccess;
|
|
}
|
|
|
|
static ncclResult_t ncclSocketProgressOpt(int op, struct ncclSocket* sock, void* ptr, int size, int* offset, int block, int* closed) {
|
|
int bytes = 0;
|
|
*closed = 0;
|
|
char* data = (char*)ptr;
|
|
char line[SOCKET_NAME_MAXLEN+1];
|
|
do {
|
|
if (op == NCCL_SOCKET_RECV) bytes = recv(sock->fd, data+(*offset), size-(*offset), block ? 0 : MSG_DONTWAIT);
|
|
if (op == NCCL_SOCKET_SEND) bytes = send(sock->fd, data+(*offset), size-(*offset), block ? 0 : MSG_DONTWAIT);
|
|
if (op == NCCL_SOCKET_RECV && bytes == 0) {
|
|
*closed = 1;
|
|
return ncclSuccess;
|
|
}
|
|
if (bytes == -1) {
|
|
if (errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN) {
|
|
WARN("Net : Call to recv from %s failed : %s", ncclSocketToString(&sock->addr, line), strerror(errno));
|
|
return ncclRemoteError;
|
|
} else {
|
|
bytes = 0;
|
|
}
|
|
}
|
|
(*offset) += bytes;
|
|
if (sock->abortFlag && *sock->abortFlag != 0) {
|
|
INFO(NCCL_NET, "Socket progress: abort called");
|
|
return ncclSystemError;
|
|
}
|
|
} while (bytes > 0 && (*offset) < size);
|
|
return ncclSuccess;
|
|
}
|
|
|
|
ncclResult_t ncclSocketProgress(int op, struct ncclSocket* sock, void* ptr, int size, int* offset) {
|
|
int closed;
|
|
NCCLCHECK(ncclSocketProgressOpt(op, sock, ptr, size, offset, 0, &closed));
|
|
if (closed) {
|
|
char line[SOCKET_NAME_MAXLEN+1];
|
|
WARN("Net : Connection closed by remote peer %s", ncclSocketToString(&sock->addr, line, 0));
|
|
return ncclRemoteError;
|
|
}
|
|
return ncclSuccess;
|
|
}
|
|
|
|
ncclResult_t ncclSocketWait(int op, struct ncclSocket* sock, void* ptr, int size, int* offset) {
|
|
while (*offset < size)
|
|
NCCLCHECK(ncclSocketProgress(op, sock, ptr, size, offset));
|
|
return ncclSuccess;
|
|
}
|
|
|
|
ncclResult_t ncclSocketSend(struct ncclSocket* sock, void* ptr, int size) {
|
|
int offset = 0;
|
|
NCCLCHECK(ncclSocketWait(NCCL_SOCKET_SEND, sock, ptr, size, &offset));
|
|
return ncclSuccess;
|
|
}
|
|
|
|
ncclResult_t ncclSocketRecv(struct ncclSocket* sock, void* ptr, int size) {
|
|
int offset = 0;
|
|
NCCLCHECK(ncclSocketWait(NCCL_SOCKET_RECV, sock, ptr, size, &offset));
|
|
return ncclSuccess;
|
|
}
|
|
|
|
// Receive or detect connection closed
|
|
ncclResult_t ncclSocketTryRecv(struct ncclSocket* sock, void* ptr, int size, int* closed) {
|
|
int offset = 0;
|
|
*closed = 0;
|
|
while (offset < size) {
|
|
NCCLCHECK(ncclSocketProgressOpt(NCCL_SOCKET_RECV, sock, ptr, size, &offset, 0, closed));
|
|
if (*closed) return ncclSuccess;
|
|
}
|
|
return ncclSuccess;
|
|
}
|