#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <poll.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <limits.h>
#include "sock.h"
#include "sock_util.h"
#include <fi_iov.h>
#define SOCK_LOG_DBG(...) _SOCK_LOG_DBG(FI_LOG_EP_DATA, __VA_ARGS__)
#define SOCK_LOG_ERROR(...) _SOCK_LOG_ERROR(FI_LOG_EP_DATA, __VA_ARGS__)
ssize_t sock_ep_tx_atomic(struct fid_ep *ep,
const struct fi_msg_atomic *msg,
const struct fi_ioc *comparev, void **compare_desc,
size_t compare_count, struct fi_ioc *resultv,
void **result_desc, size_t result_count, uint64_t flags)
{
ssize_t ret;
size_t i;
size_t datatype_sz;
struct sock_op tx_op;
union sock_iov tx_iov;
struct sock_conn *conn;
struct sock_tx_ctx *tx_ctx;
uint64_t total_len, src_len, dst_len, cmp_len, op_flags;
struct sock_ep *sock_ep;
struct sock_ep_attr *ep_attr;
switch (ep->fid.fclass) {
case FI_CLASS_EP:
sock_ep = container_of(ep, struct sock_ep, ep);
tx_ctx = sock_ep->attr->tx_ctx->use_shared ?
sock_ep->attr->tx_ctx->stx_ctx : sock_ep->attr->tx_ctx;
ep_attr = sock_ep->attr;
op_flags = sock_ep->tx_attr.op_flags;
break;
case FI_CLASS_TX_CTX:
tx_ctx = container_of(ep, struct sock_tx_ctx, fid.ctx);
ep_attr = tx_ctx->ep_attr;
op_flags = tx_ctx->attr.op_flags;
break;
default:
SOCK_LOG_ERROR("Invalid EP type\n");
return -FI_EINVAL;
}
if (msg->iov_count > SOCK_EP_MAX_IOV_LIMIT ||
msg->rma_iov_count > SOCK_EP_MAX_IOV_LIMIT)
return -FI_EINVAL;
if (!tx_ctx->enabled)
return -FI_EOPBADSTATE;
ret = sock_ep_get_conn(ep_attr, tx_ctx, msg->addr, &conn);
if (ret)
return ret;
SOCK_EP_SET_TX_OP_FLAGS(flags);
if (flags & SOCK_USE_OP_FLAGS)
flags |= op_flags;
if (msg->op == FI_ATOMIC_READ) {
flags &= ~FI_INJECT;
}
if (flags & FI_TRIGGER) {
ret = sock_queue_atomic_op(ep, msg, comparev, compare_count,
resultv, result_count, flags,
FI_OP_ATOMIC);
if (ret != 1)
return ret;
}
src_len = cmp_len = 0;
datatype_sz = fi_datatype_size(msg->datatype);
for (i = 0; i < compare_count; i++)
cmp_len += (comparev[i].count * datatype_sz);
if (flags & FI_INJECT) {
for (i = 0; i < msg->iov_count; i++)
src_len += (msg->msg_iov[i].count * datatype_sz);
if ((src_len + cmp_len) > SOCK_EP_MAX_INJECT_SZ)
return -FI_EINVAL;
total_len = src_len + cmp_len;
} else {
total_len = msg->iov_count * sizeof(union sock_iov);
}
total_len += (sizeof(struct sock_op_send) +
(msg->rma_iov_count * sizeof(union sock_iov)) +
(result_count * sizeof(union sock_iov)));
sock_tx_ctx_start(tx_ctx);
if (ofi_rbavail(&tx_ctx->rb) < total_len) {
ret = -FI_EAGAIN;
goto err;
}
memset(&tx_op, 0, sizeof(tx_op));
tx_op.op = SOCK_OP_ATOMIC;
tx_op.dest_iov_len = msg->rma_iov_count;
tx_op.atomic.op = msg->op;
tx_op.atomic.datatype = msg->datatype;
tx_op.atomic.res_iov_len = result_count;
tx_op.atomic.cmp_iov_len = compare_count;
if (flags & FI_INJECT) {
tx_op.src_iov_len = src_len;
tx_op.atomic.cmp_iov_len = cmp_len;
} else {
tx_op.src_iov_len = msg->iov_count;
}
sock_tx_ctx_write_op_send(tx_ctx, &tx_op, flags,
(uintptr_t) msg->context, msg->addr,
(uintptr_t) msg->msg_iov[0].addr, ep_attr, conn);
if (flags & FI_REMOTE_CQ_DATA)
sock_tx_ctx_write(tx_ctx, &msg->data, sizeof(uint64_t));
src_len = dst_len = 0;
if (flags & FI_INJECT) {
for (i = 0; i < msg->iov_count; i++) {
sock_tx_ctx_write(tx_ctx, msg->msg_iov[i].addr,
msg->msg_iov[i].count * datatype_sz);
src_len += (msg->msg_iov[i].count * datatype_sz);
}
for (i = 0; i < compare_count; i++) {
sock_tx_ctx_write(tx_ctx, comparev[i].addr,
comparev[i].count * datatype_sz);
dst_len += comparev[i].count * datatype_sz;
}
} else {
for (i = 0; i < msg->iov_count; i++) {
tx_iov.ioc.addr = (uintptr_t) msg->msg_iov[i].addr;
tx_iov.ioc.count = msg->msg_iov[i].count;
sock_tx_ctx_write(tx_ctx, &tx_iov, sizeof(tx_iov));
src_len += (tx_iov.ioc.count * datatype_sz);
}
for (i = 0; i < compare_count; i++) {
tx_iov.ioc.addr = (uintptr_t) comparev[i].addr;
tx_iov.ioc.count = comparev[i].count;
sock_tx_ctx_write(tx_ctx, &tx_iov, sizeof(tx_iov));
dst_len += (tx_iov.ioc.count * datatype_sz);
}
}
#if ENABLE_DEBUG
if ((src_len > SOCK_EP_MAX_ATOMIC_SZ) ||
(dst_len > SOCK_EP_MAX_ATOMIC_SZ)) {
SOCK_LOG_ERROR("Max atomic operation size exceeded!\n");
ret = -FI_EINVAL;
goto err;
} else if (compare_count && (dst_len != src_len)) {
SOCK_LOG_ERROR("Buffer length mismatch\n");
ret = -FI_EINVAL;
goto err;
}
#endif
dst_len = 0;
for (i = 0; i < msg->rma_iov_count; i++) {
tx_iov.ioc.addr = msg->rma_iov[i].addr;
tx_iov.ioc.key = msg->rma_iov[i].key;
tx_iov.ioc.count = msg->rma_iov[i].count;
sock_tx_ctx_write(tx_ctx, &tx_iov, sizeof(tx_iov));
dst_len += (tx_iov.ioc.count * datatype_sz);
}
if (msg->iov_count && (dst_len != src_len)) {
SOCK_LOG_ERROR("Buffer length mismatch\n");
ret = -FI_EINVAL;
goto err;
} else {
src_len = dst_len;
}
dst_len = 0;
for (i = 0; i < result_count; i++) {
tx_iov.ioc.addr = (uintptr_t) resultv[i].addr;
tx_iov.ioc.count = resultv[i].count;
sock_tx_ctx_write(tx_ctx, &tx_iov, sizeof(tx_iov));
dst_len += (tx_iov.ioc.count * datatype_sz);
}
#if ENABLE_DEBUG
if (result_count && (dst_len != src_len)) {
SOCK_LOG_ERROR("Buffer length mismatch\n");
ret = -FI_EINVAL;
goto err;
}
#endif
sock_tx_ctx_commit(tx_ctx);
return 0;
err:
sock_tx_ctx_abort(tx_ctx);
return ret;
}
static ssize_t sock_ep_atomic_writemsg(struct fid_ep *ep,
const struct fi_msg_atomic *msg, uint64_t flags)
{
#if ENABLE_DEBUG
switch (msg->op) {
case FI_MIN:
case FI_MAX:
case FI_SUM:
case FI_PROD:
case FI_LOR:
case FI_LAND:
case FI_BOR:
case FI_BAND:
case FI_LXOR:
case FI_BXOR:
case FI_ATOMIC_WRITE:
break;
default:
SOCK_LOG_ERROR("Invalid operation type\n");
return -FI_EINVAL;
}
#endif
return sock_ep_tx_atomic(ep, msg, NULL, NULL, 0,
NULL, NULL, 0, flags);
}
static ssize_t sock_ep_atomic_write(struct fid_ep *ep,
const void *buf, size_t count, void *desc,
fi_addr_t dest_addr, uint64_t addr,
uint64_t key, enum fi_datatype datatype,
enum fi_op op, void *context)
{
struct fi_msg_atomic msg;
struct fi_ioc msg_iov;
struct fi_rma_ioc rma_iov;
msg_iov.addr = (void *)buf;
msg_iov.count = count;
msg.msg_iov = &msg_iov;
msg.desc = &desc;
msg.iov_count = 1;
msg.addr = dest_addr;
rma_iov.addr = addr;
rma_iov.key = key;
rma_iov.count = count;
msg.rma_iov = &rma_iov;
msg.rma_iov_count = 1;
msg.datatype = datatype;
msg.op = op;
msg.context = context;
msg.data = 0;
return sock_ep_atomic_writemsg(ep, &msg, SOCK_USE_OP_FLAGS);
}
static ssize_t sock_ep_atomic_writev(struct fid_ep *ep,
const struct fi_ioc *iov, void **desc, size_t count,
fi_addr_t dest_addr,
uint64_t addr, uint64_t key,
enum fi_datatype datatype, enum fi_op op, void *context)
{
int i;
struct fi_msg_atomic msg;
struct fi_rma_ioc rma_iov;
msg.msg_iov = iov;
msg.desc = desc;
msg.iov_count = count;
msg.addr = dest_addr;
rma_iov.addr = addr;
rma_iov.key = key;
for (i = 0, rma_iov.count = 0; i < count; i++)
rma_iov.count += iov[i].count;
msg.rma_iov = &rma_iov;
msg.rma_iov_count = 1;
msg.datatype = datatype;
msg.op = op;
msg.context = context;
msg.data = 0;
return sock_ep_atomic_writemsg(ep, &msg, SOCK_USE_OP_FLAGS);
}
static ssize_t sock_ep_atomic_inject(struct fid_ep *ep, const void *buf,
size_t count, fi_addr_t dest_addr, uint64_t addr,
uint64_t key, enum fi_datatype datatype,
enum fi_op op)
{
struct fi_msg_atomic msg;
struct fi_ioc msg_iov;
struct fi_rma_ioc rma_iov;
msg_iov.addr = (void *)buf;
msg_iov.count = count;
msg.msg_iov = &msg_iov;
msg.iov_count = 1;
msg.addr = dest_addr;
rma_iov.addr = addr;
rma_iov.key = key;
rma_iov.count = count;
msg.rma_iov = &rma_iov;
msg.rma_iov_count = 1;
msg.datatype = datatype;
msg.op = op;
msg.data = 0;
return sock_ep_atomic_writemsg(ep, &msg, FI_INJECT |
SOCK_NO_COMPLETION | SOCK_USE_OP_FLAGS);
}
static ssize_t sock_ep_atomic_readwritemsg(struct fid_ep *ep,
const struct fi_msg_atomic *msg,
struct fi_ioc *resultv, void **result_desc,
size_t result_count, uint64_t flags)
{
switch (msg->op) {
case FI_MIN:
case FI_MAX:
case FI_SUM:
case FI_PROD:
case FI_LOR:
case FI_LAND:
case FI_BOR:
case FI_BAND:
case FI_LXOR:
case FI_BXOR:
case FI_ATOMIC_READ:
case FI_ATOMIC_WRITE:
break;
default:
SOCK_LOG_ERROR("Invalid operation type\n");
return -FI_EINVAL;
}
return sock_ep_tx_atomic(ep, msg, NULL, NULL, 0,
resultv, result_desc, result_count, flags);
}
static ssize_t sock_ep_atomic_readwrite(struct fid_ep *ep,
const void *buf, size_t count, void *desc,
void *result, void *result_desc,
fi_addr_t dest_addr,
uint64_t addr, uint64_t key,
enum fi_datatype datatype, enum fi_op op, void *context)
{
struct fi_msg_atomic msg;
struct fi_ioc msg_iov;
struct fi_rma_ioc rma_iov;
struct fi_ioc resultv;
if (!buf && op != FI_ATOMIC_READ)
return -FI_EINVAL;
if (op == FI_ATOMIC_READ)
msg_iov.addr = NULL;
else
msg_iov.addr = (void *)buf;
msg_iov.count = count;
msg.msg_iov = &msg_iov;
msg.desc = &desc;
msg.iov_count = 1;
msg.addr = dest_addr;
rma_iov.addr = addr;
rma_iov.count = count;
rma_iov.key = key;
msg.rma_iov = &rma_iov;
msg.rma_iov_count = 1;
msg.datatype = datatype;
msg.op = op;
msg.context = context;
resultv.addr = result;
resultv.count = count;
return sock_ep_atomic_readwritemsg(ep, &msg, &resultv, &result_desc, 1,
SOCK_USE_OP_FLAGS);
}
static ssize_t sock_ep_atomic_readwritev(struct fid_ep *ep,
const struct fi_ioc *iov, void **desc, size_t count,
struct fi_ioc *resultv, void **result_desc,
size_t result_count, fi_addr_t dest_addr,
uint64_t addr, uint64_t key,
enum fi_datatype datatype, enum fi_op op, void *context)
{
struct fi_msg_atomic msg;
struct fi_rma_ioc rma_iov;
msg.msg_iov = iov;
msg.desc = desc;
msg.iov_count = count;
msg.addr = dest_addr;
rma_iov.addr = addr;
rma_iov.count = ofi_total_ioc_cnt(iov, count);
rma_iov.key = key;
msg.rma_iov = &rma_iov;
msg.rma_iov_count = 1;
msg.datatype = datatype;
msg.op = op;
msg.context = context;
return sock_ep_atomic_readwritemsg(ep, &msg,
resultv, result_desc, result_count,
SOCK_USE_OP_FLAGS);
}
static ssize_t sock_ep_atomic_compwritemsg(struct fid_ep *ep,
const struct fi_msg_atomic *msg,
const struct fi_ioc *comparev, void **compare_desc,
size_t compare_count, struct fi_ioc *resultv,
void **result_desc, size_t result_count, uint64_t flags)
{
switch (msg->op) {
case FI_CSWAP:
case FI_CSWAP_NE:
case FI_CSWAP_LE:
case FI_CSWAP_LT:
case FI_CSWAP_GE:
case FI_CSWAP_GT:
case FI_MSWAP:
break;
default:
SOCK_LOG_ERROR("Invalid operation type\n");
return -FI_EINVAL;
}
return sock_ep_tx_atomic(ep, msg, comparev, compare_desc, compare_count,
resultv, result_desc, result_count, flags);
}
static ssize_t sock_ep_atomic_compwrite(struct fid_ep *ep,
const void *buf, size_t count, void *desc,
const void *compare, void *compare_desc,
void *result, void *result_desc,
fi_addr_t dest_addr,
uint64_t addr, uint64_t key,
enum fi_datatype datatype, enum fi_op op, void *context)
{
struct fi_msg_atomic msg;
struct fi_ioc msg_iov;
struct fi_rma_ioc rma_iov;
struct fi_ioc resultv;
struct fi_ioc comparev;
msg_iov.addr = (void *)buf;
msg_iov.count = count;
msg.msg_iov = &msg_iov;
msg.desc = &desc;
msg.iov_count = 1;
msg.addr = dest_addr;
rma_iov.addr = addr;
rma_iov.count = count;
rma_iov.key = key;
msg.rma_iov = &rma_iov;
msg.rma_iov_count = 1;
msg.datatype = datatype;
msg.op = op;
msg.context = context;
resultv.addr = result;
resultv.count = count;
comparev.addr = (void *)compare;
comparev.count = count;
return sock_ep_atomic_compwritemsg(ep, &msg, &comparev, &compare_desc,
1, &resultv, &result_desc, 1, SOCK_USE_OP_FLAGS);
}
static ssize_t sock_ep_atomic_compwritev(struct fid_ep *ep,
const struct fi_ioc *iov, void **desc, size_t count,
const struct fi_ioc *comparev, void **compare_desc,
size_t compare_count, struct fi_ioc *resultv,
void **result_desc, size_t result_count,
fi_addr_t dest_addr, uint64_t addr, uint64_t key,
enum fi_datatype datatype, enum fi_op op, void *context)
{
struct fi_msg_atomic msg;
struct fi_rma_ioc rma_iov;
msg.msg_iov = iov;
msg.desc = desc;
msg.iov_count = count;
msg.addr = dest_addr;
rma_iov.addr = addr;
rma_iov.count = ofi_total_ioc_cnt(iov, count);
rma_iov.key = key;
msg.rma_iov = &rma_iov;
msg.rma_iov_count = 1;
msg.datatype = datatype;
msg.op = op;
msg.context = context;
return sock_ep_atomic_compwritemsg(ep, &msg,
comparev, compare_desc, compare_count,
resultv, result_desc, result_count,
SOCK_USE_OP_FLAGS);
}
int sock_query_atomic(struct fid_domain *domain,
enum fi_datatype datatype, enum fi_op op,
struct fi_atomic_attr *attr, uint64_t flags)
{
if (flags)
return -FI_ENOSYS;
switch (datatype) {
case FI_FLOAT:
case FI_DOUBLE:
case FI_LONG_DOUBLE:
if (op == FI_BOR || op == FI_BAND ||
op == FI_BXOR || op == FI_MSWAP)
return -FI_EOPNOTSUPP;
break;
case FI_FLOAT_COMPLEX:
case FI_DOUBLE_COMPLEX:
case FI_LONG_DOUBLE_COMPLEX:
if (op == FI_BOR || op == FI_BAND ||
op == FI_BXOR || op == FI_MSWAP ||
op == FI_MIN || op == FI_MAX ||
op == FI_CSWAP_LE || op == FI_CSWAP_LT ||
op == FI_CSWAP_GE || op == FI_CSWAP_GT)
return -FI_EOPNOTSUPP;
break;
default:
break;
}
attr->size = fi_datatype_size(datatype);
if (attr->size == 0)
return -FI_EINVAL;
attr->count = (SOCK_EP_MAX_ATOMIC_SZ / attr->size);
return 0;
}
static int sock_ep_atomic_valid(struct fid_ep *ep, enum fi_datatype datatype,
enum fi_op op, size_t *count)
{
struct fi_atomic_attr attr;
int ret;
ret = sock_query_atomic(NULL, datatype, op, &attr, 0);
if (!ret)
*count = attr.count;
return ret;
}
struct fi_ops_atomic sock_ep_atomic = {
.size = sizeof(struct fi_ops_atomic),
.write = sock_ep_atomic_write,
.writev = sock_ep_atomic_writev,
.writemsg = sock_ep_atomic_writemsg,
.inject = sock_ep_atomic_inject,
.readwrite = sock_ep_atomic_readwrite,
.readwritev = sock_ep_atomic_readwritev,
.readwritemsg = sock_ep_atomic_readwritemsg,
.compwrite = sock_ep_atomic_compwrite,
.compwritev = sock_ep_atomic_compwritev,
.compwritemsg = sock_ep_atomic_compwritemsg,
.writevalid = sock_ep_atomic_valid,
.readwritevalid = sock_ep_atomic_valid,
.compwritevalid = sock_ep_atomic_valid,
};