/**
* Copyright (C) Mellanox Technologies Ltd. 2017-219. ALL RIGHTS RESERVED.
* Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
* See file LICENSE for terms.
*/
#include "rdmacm_md.h"
#include "rdmacm_cm.h"
static ucs_config_field_t uct_rdmacm_md_config_table[] = {
{"", "", NULL,
ucs_offsetof(uct_rdmacm_md_config_t, super), UCS_CONFIG_TYPE_TABLE(uct_md_config_table)},
{"ADDR_RESOLVE_TIMEOUT", "500ms",
"Time to wait for address resolution to complete",
ucs_offsetof(uct_rdmacm_md_config_t, addr_resolve_timeout), UCS_CONFIG_TYPE_TIME},
{NULL}
};
static void uct_rdmacm_md_close(uct_md_h md);
static uct_md_ops_t uct_rdmacm_md_ops = {
.close = uct_rdmacm_md_close,
.query = uct_rdmacm_md_query,
.is_sockaddr_accessible = uct_rdmacm_is_sockaddr_accessible,
.detect_memory_type = ucs_empty_function_return_unsupported,
};
static void uct_rdmacm_md_close(uct_md_h md)
{
uct_rdmacm_md_t *rdmacm_md = ucs_derived_of(md, uct_rdmacm_md_t);
ucs_free(rdmacm_md);
}
ucs_status_t uct_rdmacm_md_query(uct_md_h md, uct_md_attr_t *md_attr)
{
md_attr->cap.flags = UCT_MD_FLAG_SOCKADDR;
md_attr->cap.reg_mem_types = 0;
md_attr->cap.access_mem_type = UCS_MEMORY_TYPE_HOST;
md_attr->cap.detect_mem_types = 0;
md_attr->cap.max_alloc = 0;
md_attr->cap.max_reg = 0;
md_attr->rkey_packed_size = 0;
md_attr->reg_cost.overhead = 0;
md_attr->reg_cost.growth = 0;
memset(&md_attr->local_cpus, 0xff, sizeof(md_attr->local_cpus));
return UCS_OK;
}
static enum rdma_cm_event_type
uct_rdmacm_get_event_type(struct rdma_event_channel *event_ch)
{
enum rdma_cm_event_type event_type;
struct rdma_cm_event *event;
int ret;
/* Fetch an event */
ret = rdma_get_cm_event(event_ch, &event);
if (ret) {
ucs_warn("rdma_get_cm_event() failed: %m");
return RDMA_CM_EVENT_ADDR_RESOLVED;
}
event_type = event->event;
ret = rdma_ack_cm_event(event);
if (ret) {
ucs_warn("rdma_ack_cm_event() failed. event status: %d. %m.", event->status);
}
return event_type;
}
static int uct_rdmacm_is_addr_route_resolved(struct rdma_cm_id *cm_id,
struct sockaddr *addr,
int timeout_ms)
{
char ip_port_str[UCS_SOCKADDR_STRING_LEN];
enum rdma_cm_event_type event_type;
ucs_status_t status;
status = uct_rdmacm_resolve_addr(cm_id, addr, timeout_ms, UCS_LOG_LEVEL_DEBUG);
if (status != UCS_OK) {
return 0;
}
event_type = uct_rdmacm_get_event_type(cm_id->channel);
if (event_type != RDMA_CM_EVENT_ADDR_RESOLVED) {
ucs_debug("failed to resolve address (addr = %s). RDMACM event %s.",
ucs_sockaddr_str(addr, ip_port_str, UCS_SOCKADDR_STRING_LEN),
rdma_event_str(event_type));
return 0;
}
if (cm_id->verbs->device->transport_type == IBV_TRANSPORT_IWARP) {
ucs_debug("%s: iWarp support is not implemented",
ucs_sockaddr_str(addr, ip_port_str, UCS_SOCKADDR_STRING_LEN));
return 0;
}
if (rdma_resolve_route(cm_id, timeout_ms)) {
ucs_debug("rdma_resolve_route(addr = %s) failed: %m",
ucs_sockaddr_str(addr, ip_port_str, UCS_SOCKADDR_STRING_LEN));
return 0;
}
event_type = uct_rdmacm_get_event_type(cm_id->channel);
if (event_type != RDMA_CM_EVENT_ROUTE_RESOLVED) {
ucs_debug("failed to resolve route to addr = %s. RDMACM event %s.",
ucs_sockaddr_str(addr, ip_port_str, UCS_SOCKADDR_STRING_LEN),
rdma_event_str(event_type));
return 0;
}
return 1;
}
int uct_rdmacm_is_sockaddr_accessible(uct_md_h md, const ucs_sock_addr_t *sockaddr,
uct_sockaddr_accessibility_t mode)
{
uct_rdmacm_md_t *rdmacm_md = ucs_derived_of(md, uct_rdmacm_md_t);
struct rdma_event_channel *event_ch = NULL;
struct rdma_cm_id *cm_id = NULL;
int is_accessible = 0;
char ip_port_str[UCS_SOCKADDR_STRING_LEN];
if ((mode != UCT_SOCKADDR_ACC_LOCAL) && (mode != UCT_SOCKADDR_ACC_REMOTE)) {
ucs_error("Unknown sockaddr accessibility mode %d", mode);
return 0;
}
event_ch = rdma_create_event_channel();
if (event_ch == NULL) {
ucs_error("rdma_create_event_channel() failed: %m");
goto out;
}
if (rdma_create_id(event_ch, &cm_id, NULL, RDMA_PS_UDP)) {
ucs_error("rdma_create_id() failed: %m");
goto out_destroy_event_channel;
}
if (mode == UCT_SOCKADDR_ACC_LOCAL) {
/* Server side to check if can bind to the given sockaddr */
if (rdma_bind_addr(cm_id, (struct sockaddr *)sockaddr->addr)) {
ucs_debug("rdma_bind_addr(addr = %s) failed: %m",
ucs_sockaddr_str((struct sockaddr *)sockaddr->addr,
ip_port_str, UCS_SOCKADDR_STRING_LEN));
goto out_destroy_id;
}
if (ucs_sockaddr_is_inaddr_any((struct sockaddr *)sockaddr->addr)) {
is_accessible = 1;
goto out_print;
}
}
/* Client and server sides check if can access the given sockaddr.
* The timeout needs to be passed in ms */
is_accessible = uct_rdmacm_is_addr_route_resolved(cm_id,
(struct sockaddr *)sockaddr->addr,
UCS_MSEC_PER_SEC * rdmacm_md->addr_resolve_timeout);
if (!is_accessible) {
goto out_destroy_id;
}
out_print:
ucs_debug("address %s (port %d) is accessible from rdmacm_md %p with mode: %d",
ucs_sockaddr_str((struct sockaddr *)sockaddr->addr, ip_port_str,
UCS_SOCKADDR_STRING_LEN),
ntohs(rdma_get_src_port(cm_id)), rdmacm_md, mode);
out_destroy_id:
rdma_destroy_id(cm_id);
out_destroy_event_channel:
rdma_destroy_event_channel(event_ch);
out:
return is_accessible;
}
static ucs_status_t
uct_rdmacm_query_md_resources(uct_component_t *component,
uct_md_resource_desc_t **resources_p,
unsigned *num_resources_p)
{
struct rdma_event_channel *event_ch = NULL;
/* Create a dummy event channel to check if RDMACM can be used */
event_ch = rdma_create_event_channel();
if (event_ch == NULL) {
ucs_debug("could not create an RDMACM event channel. %m. "
"Disabling the RDMACM resource");
return uct_md_query_empty_md_resource(resources_p, num_resources_p);
}
rdma_destroy_event_channel(event_ch);
return uct_md_query_single_md_resource(component, resources_p,
num_resources_p);
}
static ucs_status_t
uct_rdmacm_md_open(uct_component_t *component, const char *md_name,
const uct_md_config_t *uct_md_config, uct_md_h *md_p)
{
uct_rdmacm_md_config_t *md_config = ucs_derived_of(uct_md_config,
uct_rdmacm_md_config_t);
uct_rdmacm_md_t *md;
ucs_status_t status;
md = ucs_malloc(sizeof(*md), "rdmacm_md");
if (md == NULL) {
status = UCS_ERR_NO_MEMORY;
goto out;
}
md->super.ops = &uct_rdmacm_md_ops;
md->super.component = &uct_rdmacm_component;
md->addr_resolve_timeout = md_config->addr_resolve_timeout;
/* cppcheck-suppress autoVariables */
*md_p = &md->super;
status = UCS_OK;
out:
return status;
}
uct_component_t uct_rdmacm_component = {
.query_md_resources = uct_rdmacm_query_md_resources,
.md_open = uct_rdmacm_md_open,
#if HAVE_RDMACM_QP_LESS
.cm_open = UCS_CLASS_NEW_FUNC_NAME(uct_rdmacm_cm_t),
#else
.cm_open = ucs_empty_function_return_unsupported,
#endif
.rkey_unpack = ucs_empty_function_return_unsupported,
.rkey_ptr = ucs_empty_function_return_unsupported,
.rkey_release = ucs_empty_function_return_success,
.name = "rdmacm",
.md_config = {
.name = "RDMA-CM memory domain",
.prefix = "IB_",
.table = uct_rdmacm_md_config_table,
.size = sizeof(uct_rdmacm_md_config_t),
},
.cm_config = {
.name = "RDMA-CM connection manager",
.prefix = "RDMACM_",
.table = uct_cm_config_table,
.size = sizeof(uct_cm_config_t),
},
.tl_list = UCT_COMPONENT_TL_LIST_INITIALIZER(&uct_rdmacm_component),
#if HAVE_RDMACM_QP_LESS
.flags = UCT_COMPONENT_FLAG_CM
#else
.flags = 0
#endif
};
UCT_COMPONENT_REGISTER(&uct_rdmacm_component)