/* * Copyright (c) 2010-2012 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include #include #include #include "cma.h" #include "acm.h" #include #include #include static pthread_mutex_t acm_lock = PTHREAD_MUTEX_INITIALIZER; static int sock = -1; static uint16_t server_port; static int ucma_set_server_port(void) { FILE *f; if ((f = fopen(IBACM_PORT_FILE, "r" STREAM_CLOEXEC))) { if (fscanf(f, "%" SCNu16, &server_port) != 1) server_port = 0; fclose(f); } else server_port = 0; return server_port; } void ucma_ib_init(void) { union { struct sockaddr any; struct sockaddr_in inet; struct sockaddr_un unx; } addr; static int init; int ret; if (init) return; pthread_mutex_lock(&acm_lock); if (init) goto unlock; if (ucma_set_server_port()) { sock = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP); if (sock < 0) goto out; memset(&addr, 0, sizeof(addr)); addr.any.sa_family = AF_INET; addr.inet.sin_addr.s_addr = htobe32(INADDR_LOOPBACK); addr.inet.sin_port = htobe16(server_port); ret = connect(sock, &addr.any, sizeof(addr.inet)); if (ret) { close(sock); sock = -1; } } else { sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); if (sock < 0) goto out; memset(&addr, 0, sizeof(addr)); addr.any.sa_family = AF_UNIX; BUILD_ASSERT(sizeof(IBACM_SERVER_PATH) <= sizeof(addr.unx.sun_path)); strcpy(addr.unx.sun_path, IBACM_SERVER_PATH); ret = connect(sock, &addr.any, sizeof(addr.unx)); if (ret) { close(sock); sock = -1; } } out: init = 1; unlock: pthread_mutex_unlock(&acm_lock); } void ucma_ib_cleanup(void) { if (sock >= 0) { shutdown(sock, SHUT_RDWR); close(sock); } } static int ucma_ib_set_addr(struct rdma_addrinfo *ib_rai, struct rdma_addrinfo *rai) { struct sockaddr_ib *src, *dst; struct ibv_path_record *path; src = calloc(1, sizeof(*src)); if (!src) return ERR(ENOMEM); dst = calloc(1, sizeof(*dst)); if (!dst) { free(src); return ERR(ENOMEM); } path = &((struct ibv_path_data *) ib_rai->ai_route)->path; src->sib_family = AF_IB; src->sib_pkey = path->pkey; src->sib_flowinfo = htobe32(be32toh(path->flowlabel_hoplimit) >> 8); memcpy(&src->sib_addr, &path->sgid, 16); ucma_set_sid(ib_rai->ai_port_space, rai->ai_src_addr, src); dst->sib_family = AF_IB; dst->sib_pkey = path->pkey; dst->sib_flowinfo = htobe32(be32toh(path->flowlabel_hoplimit) >> 8); memcpy(&dst->sib_addr, &path->dgid, 16); ucma_set_sid(ib_rai->ai_port_space, rai->ai_dst_addr, dst); ib_rai->ai_src_addr = (struct sockaddr *) src; ib_rai->ai_src_len = sizeof(*src); ib_rai->ai_dst_addr = (struct sockaddr *) dst; ib_rai->ai_dst_len = sizeof(*dst); return 0; } static int ucma_ib_set_connect(struct rdma_addrinfo *ib_rai, struct rdma_addrinfo *rai) { struct ib_connect_hdr *hdr; if (rai->ai_family == AF_IB) return 0; hdr = calloc(1, sizeof(*hdr)); if (!hdr) return ERR(ENOMEM); if (rai->ai_family == AF_INET) { hdr->ip_version = 4 << 4; memcpy(&hdr->cma_src_ip4, &((struct sockaddr_in *) rai->ai_src_addr)->sin_addr, 4); memcpy(&hdr->cma_dst_ip4, &((struct sockaddr_in *) rai->ai_dst_addr)->sin_addr, 4); } else { hdr->ip_version = 6 << 4; memcpy(&hdr->cma_src_ip6, &((struct sockaddr_in6 *) rai->ai_src_addr)->sin6_addr, 16); memcpy(&hdr->cma_dst_ip6, &((struct sockaddr_in6 *) rai->ai_dst_addr)->sin6_addr, 16); } ib_rai->ai_connect = hdr; ib_rai->ai_connect_len = sizeof(*hdr); return 0; } static void ucma_resolve_af_ib(struct rdma_addrinfo **rai) { struct rdma_addrinfo *ib_rai; ib_rai = calloc(1, sizeof(*ib_rai)); if (!ib_rai) return; ib_rai->ai_flags = (*rai)->ai_flags; ib_rai->ai_family = AF_IB; ib_rai->ai_qp_type = (*rai)->ai_qp_type; ib_rai->ai_port_space = (*rai)->ai_port_space; ib_rai->ai_route = calloc(1, (*rai)->ai_route_len); if (!ib_rai->ai_route) goto err; memcpy(ib_rai->ai_route, (*rai)->ai_route, (*rai)->ai_route_len); ib_rai->ai_route_len = (*rai)->ai_route_len; if ((*rai)->ai_src_canonname) { ib_rai->ai_src_canonname = strdup((*rai)->ai_src_canonname); if (!ib_rai->ai_src_canonname) goto err; } if ((*rai)->ai_dst_canonname) { ib_rai->ai_dst_canonname = strdup((*rai)->ai_dst_canonname); if (!ib_rai->ai_dst_canonname) goto err; } if (ucma_ib_set_connect(ib_rai, *rai)) goto err; if (ucma_ib_set_addr(ib_rai, *rai)) goto err; ib_rai->ai_next = *rai; *rai = ib_rai; return; err: rdma_freeaddrinfo(ib_rai); } static void ucma_ib_save_resp(struct rdma_addrinfo *rai, struct acm_msg *msg) { struct acm_ep_addr_data *ep_data; struct ibv_path_data *path_data = NULL; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; int i, cnt, path_cnt = 0; cnt = (msg->hdr.length - ACM_MSG_HDR_LENGTH) / ACM_MSG_EP_LENGTH; for (i = 0; i < cnt; i++) { ep_data = &msg->resolve_data[i]; switch (ep_data->type) { case ACM_EP_INFO_PATH: ep_data->type = 0; if (!path_data) path_data = (struct ibv_path_data *) ep_data; path_cnt++; break; case ACM_EP_INFO_ADDRESS_IP: if (!(ep_data->flags & ACM_EP_FLAG_SOURCE) || rai->ai_src_len) break; sin = calloc(1, sizeof(*sin)); if (!sin) break; sin->sin_family = AF_INET; memcpy(&sin->sin_addr, &ep_data->info.addr, 4); rai->ai_src_len = sizeof(*sin); rai->ai_src_addr = (struct sockaddr *) sin; break; case ACM_EP_INFO_ADDRESS_IP6: if (!(ep_data->flags & ACM_EP_FLAG_SOURCE) || rai->ai_src_len) break; sin6 = calloc(1, sizeof(*sin6)); if (!sin6) break; sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, &ep_data->info.addr, 16); rai->ai_src_len = sizeof(*sin6); rai->ai_src_addr = (struct sockaddr *) sin6; break; default: break; } } rai->ai_route = calloc(path_cnt, sizeof(*path_data)); if (rai->ai_route) { memcpy(rai->ai_route, path_data, path_cnt * sizeof(*path_data)); rai->ai_route_len = path_cnt * sizeof(*path_data); } } static void ucma_set_ep_addr(struct acm_ep_addr_data *data, struct sockaddr *addr) { if (addr->sa_family == AF_INET) { data->type = ACM_EP_INFO_ADDRESS_IP; memcpy(data->info.addr, &((struct sockaddr_in *) addr)->sin_addr, 4); } else { data->type = ACM_EP_INFO_ADDRESS_IP6; memcpy(data->info.addr, &((struct sockaddr_in6 *) addr)->sin6_addr, 16); } } static int ucma_inet_addr(struct sockaddr *addr, socklen_t len) { return len && addr && (addr->sa_family == AF_INET || addr->sa_family == AF_INET6); } static int ucma_ib_addr(struct sockaddr *addr, socklen_t len) { return len && addr && (addr->sa_family == AF_IB); } void ucma_ib_resolve(struct rdma_addrinfo **rai, const struct rdma_addrinfo *hints) { struct acm_msg msg; struct acm_ep_addr_data *data; int ret; ucma_ib_init(); if (sock < 0) return; memset(&msg, 0, sizeof msg); msg.hdr.version = ACM_VERSION; msg.hdr.opcode = ACM_OP_RESOLVE; msg.hdr.length = ACM_MSG_HDR_LENGTH; data = &msg.resolve_data[0]; if (ucma_inet_addr((*rai)->ai_src_addr, (*rai)->ai_src_len)) { data->flags = ACM_EP_FLAG_SOURCE; ucma_set_ep_addr(data, (*rai)->ai_src_addr); data++; msg.hdr.length += ACM_MSG_EP_LENGTH; } if (ucma_inet_addr((*rai)->ai_dst_addr, (*rai)->ai_dst_len)) { data->flags = ACM_EP_FLAG_DEST; if (hints->ai_flags & (RAI_NUMERICHOST | RAI_NOROUTE)) data->flags |= ACM_FLAGS_NODELAY; ucma_set_ep_addr(data, (*rai)->ai_dst_addr); data++; msg.hdr.length += ACM_MSG_EP_LENGTH; } if (hints->ai_route_len || ucma_ib_addr((*rai)->ai_src_addr, (*rai)->ai_src_len) || ucma_ib_addr((*rai)->ai_dst_addr, (*rai)->ai_dst_len)) { struct ibv_path_record *path; if (hints->ai_route_len == sizeof(struct ibv_path_record)) path = (struct ibv_path_record *) hints->ai_route; else if (hints->ai_route_len == sizeof(struct ibv_path_data)) path = &((struct ibv_path_data *) hints->ai_route)->path; else path = NULL; if (path) memcpy(&data->info.path, path, sizeof(*path)); if (ucma_ib_addr((*rai)->ai_src_addr, (*rai)->ai_src_len)) { memcpy(&data->info.path.sgid, &((struct sockaddr_ib *) (*rai)->ai_src_addr)->sib_addr, 16); } if (ucma_ib_addr((*rai)->ai_dst_addr, (*rai)->ai_dst_len)) { memcpy(&data->info.path.dgid, &((struct sockaddr_ib *) (*rai)->ai_dst_addr)->sib_addr, 16); } data->type = ACM_EP_INFO_PATH; data++; msg.hdr.length += ACM_MSG_EP_LENGTH; } pthread_mutex_lock(&acm_lock); ret = send(sock, (char *) &msg, msg.hdr.length, 0); if (ret != msg.hdr.length) { pthread_mutex_unlock(&acm_lock); return; } ret = recv(sock, (char *) &msg, sizeof msg, 0); pthread_mutex_unlock(&acm_lock); if (ret < ACM_MSG_HDR_LENGTH || ret != msg.hdr.length || msg.hdr.status) return; ucma_ib_save_resp(*rai, &msg); if (af_ib_support && !(hints->ai_flags & RAI_ROUTEONLY) && (*rai)->ai_route_len) ucma_resolve_af_ib(rai); }