1 /**
2  * Copyright (C) Mellanox Technologies Ltd. 2017-219.  ALL RIGHTS RESERVED.
3  * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
4  * See file LICENSE for terms.
5  */
6 
7 #ifdef HAVE_CONFIG_H
8 #  include "config.h"
9 #endif
10 
11 #include "rdmacm_md.h"
12 #include "rdmacm_cm.h"
13 
14 
15 static ucs_config_field_t uct_rdmacm_md_config_table[] = {
16   {"", "", NULL,
17    ucs_offsetof(uct_rdmacm_md_config_t, super), UCS_CONFIG_TYPE_TABLE(uct_md_config_table)},
18 
19   {"ADDR_RESOLVE_TIMEOUT", "500ms",
20    "Time to wait for address resolution to complete",
21     ucs_offsetof(uct_rdmacm_md_config_t, addr_resolve_timeout), UCS_CONFIG_TYPE_TIME},
22 
23   {NULL}
24 };
25 
26 static void uct_rdmacm_md_close(uct_md_h md);
27 
28 static uct_md_ops_t uct_rdmacm_md_ops = {
29     .close                   = uct_rdmacm_md_close,
30     .query                   = uct_rdmacm_md_query,
31     .is_sockaddr_accessible  = uct_rdmacm_is_sockaddr_accessible,
32     .detect_memory_type      = ucs_empty_function_return_unsupported,
33 };
34 
uct_rdmacm_md_close(uct_md_h md)35 static void uct_rdmacm_md_close(uct_md_h md)
36 {
37     uct_rdmacm_md_t *rdmacm_md = ucs_derived_of(md, uct_rdmacm_md_t);
38     ucs_free(rdmacm_md);
39 }
40 
uct_rdmacm_md_query(uct_md_h md,uct_md_attr_t * md_attr)41 ucs_status_t uct_rdmacm_md_query(uct_md_h md, uct_md_attr_t *md_attr)
42 {
43     md_attr->cap.flags            = UCT_MD_FLAG_SOCKADDR;
44     md_attr->cap.reg_mem_types    = 0;
45     md_attr->cap.access_mem_type  = UCS_MEMORY_TYPE_HOST;
46     md_attr->cap.detect_mem_types = 0;
47     md_attr->cap.max_alloc        = 0;
48     md_attr->cap.max_reg          = 0;
49     md_attr->rkey_packed_size     = 0;
50     md_attr->reg_cost             = ucs_linear_func_make(0, 0);
51     memset(&md_attr->local_cpus, 0xff, sizeof(md_attr->local_cpus));
52     return UCS_OK;
53 }
54 
55 static enum rdma_cm_event_type
uct_rdmacm_get_event_type(struct rdma_event_channel * event_ch)56 uct_rdmacm_get_event_type(struct rdma_event_channel *event_ch)
57 {
58     enum rdma_cm_event_type event_type;
59     struct rdma_cm_event *event;
60     int ret;
61 
62     /* Fetch an event */
63     ret = rdma_get_cm_event(event_ch, &event);
64     if (ret) {
65         ucs_warn("rdma_get_cm_event() failed: %m");
66         return RDMA_CM_EVENT_ADDR_RESOLVED;
67     }
68 
69     event_type = event->event;
70     ret = rdma_ack_cm_event(event);
71     if (ret) {
72         ucs_warn("rdma_ack_cm_event() failed. event status: %d. %m.", event->status);
73     }
74 
75     return event_type;
76 }
77 
uct_rdmacm_is_addr_route_resolved(struct rdma_cm_id * cm_id,struct sockaddr * addr,int timeout_ms)78 static int uct_rdmacm_is_addr_route_resolved(struct rdma_cm_id *cm_id,
79                                              struct sockaddr *addr,
80                                              int timeout_ms)
81 {
82     char ip_port_str[UCS_SOCKADDR_STRING_LEN];
83     enum rdma_cm_event_type event_type;
84     ucs_status_t status;
85 
86     status = uct_rdmacm_resolve_addr(cm_id, addr, timeout_ms, UCS_LOG_LEVEL_DEBUG);
87     if (status != UCS_OK) {
88         return 0;
89     }
90 
91     event_type = uct_rdmacm_get_event_type(cm_id->channel);
92     if (event_type != RDMA_CM_EVENT_ADDR_RESOLVED) {
93         ucs_debug("failed to resolve address (addr = %s). RDMACM event %s.",
94                   ucs_sockaddr_str(addr, ip_port_str, UCS_SOCKADDR_STRING_LEN),
95                   rdma_event_str(event_type));
96         return 0;
97     }
98 
99     if (cm_id->verbs->device->transport_type == IBV_TRANSPORT_IWARP) {
100         ucs_debug("%s: iWarp support is not implemented",
101                   ucs_sockaddr_str(addr, ip_port_str, UCS_SOCKADDR_STRING_LEN));
102         return 0;
103     }
104 
105     if (rdma_resolve_route(cm_id, timeout_ms)) {
106         ucs_debug("rdma_resolve_route(addr = %s) failed: %m",
107                    ucs_sockaddr_str(addr, ip_port_str, UCS_SOCKADDR_STRING_LEN));
108         return 0;
109     }
110 
111     event_type = uct_rdmacm_get_event_type(cm_id->channel);
112     if (event_type != RDMA_CM_EVENT_ROUTE_RESOLVED) {
113         ucs_debug("failed to resolve route to addr = %s. RDMACM event %s.",
114                   ucs_sockaddr_str(addr, ip_port_str, UCS_SOCKADDR_STRING_LEN),
115                   rdma_event_str(event_type));
116         return 0;
117     }
118 
119     return 1;
120 }
121 
uct_rdmacm_is_sockaddr_accessible(uct_md_h md,const ucs_sock_addr_t * sockaddr,uct_sockaddr_accessibility_t mode)122 int uct_rdmacm_is_sockaddr_accessible(uct_md_h md, const ucs_sock_addr_t *sockaddr,
123                                       uct_sockaddr_accessibility_t mode)
124 {
125     uct_rdmacm_md_t *rdmacm_md = ucs_derived_of(md, uct_rdmacm_md_t);
126     struct rdma_event_channel *event_ch = NULL;
127     struct rdma_cm_id *cm_id = NULL;
128     int is_accessible = 0;
129     char ip_port_str[UCS_SOCKADDR_STRING_LEN];
130 
131     if ((mode != UCT_SOCKADDR_ACC_LOCAL) && (mode != UCT_SOCKADDR_ACC_REMOTE)) {
132         ucs_error("Unknown sockaddr accessibility mode %d", mode);
133         return 0;
134     }
135 
136     event_ch = rdma_create_event_channel();
137     if (event_ch == NULL) {
138         ucs_error("rdma_create_event_channel() failed: %m");
139         goto out;
140     }
141 
142     if (rdma_create_id(event_ch, &cm_id, NULL, RDMA_PS_UDP)) {
143         ucs_error("rdma_create_id() failed: %m");
144         goto out_destroy_event_channel;
145     }
146 
147     if (mode == UCT_SOCKADDR_ACC_LOCAL) {
148         /* Server side to check if can bind to the given sockaddr */
149         if (rdma_bind_addr(cm_id, (struct sockaddr *)sockaddr->addr)) {
150             ucs_debug("rdma_bind_addr(addr = %s) failed: %m",
151                       ucs_sockaddr_str((struct sockaddr *)sockaddr->addr,
152                                        ip_port_str, UCS_SOCKADDR_STRING_LEN));
153             goto out_destroy_id;
154         }
155 
156         if (ucs_sockaddr_is_inaddr_any((struct sockaddr *)sockaddr->addr)) {
157             is_accessible = 1;
158             goto out_print;
159         }
160     }
161 
162     /* Client and server sides check if can access the given sockaddr.
163      * The timeout needs to be passed in ms */
164     is_accessible = uct_rdmacm_is_addr_route_resolved(cm_id,
165                                                      (struct sockaddr *)sockaddr->addr,
166                                                      UCS_MSEC_PER_SEC * rdmacm_md->addr_resolve_timeout);
167     if (!is_accessible) {
168         goto out_destroy_id;
169     }
170 
171 out_print:
172     ucs_debug("address %s (port %d) is accessible from rdmacm_md %p with mode: %d",
173               ucs_sockaddr_str((struct sockaddr *)sockaddr->addr, ip_port_str,
174                                UCS_SOCKADDR_STRING_LEN),
175               ntohs(rdma_get_src_port(cm_id)), rdmacm_md, mode);
176 
177 out_destroy_id:
178     rdma_destroy_id(cm_id);
179 out_destroy_event_channel:
180     rdma_destroy_event_channel(event_ch);
181 out:
182     return is_accessible;
183 }
184 
185 static ucs_status_t
uct_rdmacm_query_md_resources(uct_component_t * component,uct_md_resource_desc_t ** resources_p,unsigned * num_resources_p)186 uct_rdmacm_query_md_resources(uct_component_t *component,
187                               uct_md_resource_desc_t **resources_p,
188                               unsigned *num_resources_p)
189 {
190     struct rdma_event_channel *event_ch = NULL;
191 
192     /* Create a dummy event channel to check if RDMACM can be used */
193     event_ch = rdma_create_event_channel();
194     if (event_ch == NULL) {
195         ucs_debug("could not create an RDMACM event channel. %m. "
196                   "Disabling the RDMACM resource");
197         return uct_md_query_empty_md_resource(resources_p, num_resources_p);
198 
199     }
200 
201     rdma_destroy_event_channel(event_ch);
202 
203     return uct_md_query_single_md_resource(component, resources_p,
204                                            num_resources_p);
205 }
206 
207 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)208 uct_rdmacm_md_open(uct_component_t *component, const char *md_name,
209                    const uct_md_config_t *uct_md_config, uct_md_h *md_p)
210 {
211     uct_rdmacm_md_config_t *md_config = ucs_derived_of(uct_md_config,
212                                                        uct_rdmacm_md_config_t);
213     uct_rdmacm_md_t *md;
214     ucs_status_t status;
215 
216     md = ucs_malloc(sizeof(*md), "rdmacm_md");
217     if (md == NULL) {
218         status = UCS_ERR_NO_MEMORY;
219         goto out;
220     }
221 
222     md->super.ops            = &uct_rdmacm_md_ops;
223     md->super.component      = &uct_rdmacm_component;
224     md->addr_resolve_timeout = md_config->addr_resolve_timeout;
225 
226     /* cppcheck-suppress autoVariables */
227     *md_p = &md->super;
228     status = UCS_OK;
229 
230 out:
231     return status;
232 }
233 
234 uct_component_t uct_rdmacm_component = {
235     .query_md_resources = uct_rdmacm_query_md_resources,
236     .md_open            = uct_rdmacm_md_open,
237 #if HAVE_RDMACM_QP_LESS
238     .cm_open            = UCS_CLASS_NEW_FUNC_NAME(uct_rdmacm_cm_t),
239 #else
240     .cm_open            = ucs_empty_function_return_unsupported,
241 #endif
242     .rkey_unpack        = ucs_empty_function_return_unsupported,
243     .rkey_ptr           = ucs_empty_function_return_unsupported,
244     .rkey_release       = ucs_empty_function_return_success,
245     .name               = "rdmacm",
246     .md_config          = {
247         .name           = "RDMA-CM memory domain",
248         .prefix         = "RDMACM_",
249         .table          = uct_rdmacm_md_config_table,
250         .size           = sizeof(uct_rdmacm_md_config_t),
251     },
252     .cm_config          = {
253         .name           = "RDMA-CM connection manager",
254         .prefix         = "RDMACM_",
255         .table          = uct_cm_config_table,
256         .size           = sizeof(uct_cm_config_t),
257     },
258     .tl_list            = UCT_COMPONENT_TL_LIST_INITIALIZER(&uct_rdmacm_component),
259 #if HAVE_RDMACM_QP_LESS
260     .flags              = UCT_COMPONENT_FLAG_CM
261 #else
262     .flags              = 0
263 #endif
264 };
265 UCT_COMPONENT_REGISTER(&uct_rdmacm_component)
266