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