| /* | |
| * Copyright (c) 2009 Intel Corporation. All rights reserved. | |
| * Copyright (c) 2013 Mellanox Technologies LTD. All rights reserved. | |
| * | |
| * This software is available to you under 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 AWV | |
| * 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 <config.h> | |
| #include <osd.h> | |
| #include "libacm.h" | |
| #include <infiniband/acm.h> | |
| #include <stdio.h> | |
| #include <errno.h> | |
| #include <netdb.h> | |
| #include <arpa/inet.h> | |
| #include <sys/socket.h> | |
| #include <sys/un.h> | |
| #include <util/util.h> | |
| static pthread_mutex_t acm_lock = PTHREAD_MUTEX_INITIALIZER; | |
| static int sock = -1; | |
| static short server_port = 6125; | |
| static void acm_set_server_port(void) | |
| { | |
| FILE *f; | |
| f = fopen(IBACM_IBACME_PORT_FILE, "r"); | |
| if (f) { | |
| if (fscanf(f, "%hu", (unsigned short *) &server_port) != 1) | |
| printf("Failed to read server port\n"); | |
| fclose(f); | |
| } | |
| } | |
| static int ib_acm_connect_open(char *dest) | |
| { | |
| struct addrinfo hint, *res; | |
| int ret; | |
| acm_set_server_port(); | |
| memset(&hint, 0, sizeof hint); | |
| hint.ai_family = AF_UNSPEC; | |
| hint.ai_protocol = IPPROTO_TCP; | |
| ret = getaddrinfo(dest, NULL, &hint, &res); | |
| if (ret) | |
| return ret; | |
| sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | |
| if (sock == -1) { | |
| ret = errno; | |
| goto freeaddr; | |
| } | |
| ((struct sockaddr_in *) res->ai_addr)->sin_port = htobe16(server_port); | |
| ret = connect(sock, res->ai_addr, res->ai_addrlen); | |
| if (ret) { | |
| close(sock); | |
| sock = -1; | |
| } | |
| freeaddr: | |
| freeaddrinfo(res); | |
| return ret; | |
| } | |
| static int ib_acm_connect_unix(char *dest) | |
| { | |
| struct sockaddr_un addr; | |
| int ret; | |
| addr.sun_family = AF_UNIX; | |
| if (dest) { | |
| if (snprintf(addr.sun_path, sizeof(addr.sun_path), | |
| "%s", dest) >= sizeof(addr.sun_path)) { | |
| errno = ENAMETOOLONG; | |
| return errno; | |
| } | |
| } else { | |
| BUILD_ASSERT(sizeof(IBACM_IBACME_SERVER_PATH) <= | |
| sizeof(addr.sun_path)); | |
| strcpy(addr.sun_path, IBACM_IBACME_SERVER_PATH); | |
| } | |
| sock = socket(AF_UNIX, SOCK_STREAM, 0); | |
| if (sock < 0) | |
| return errno; | |
| if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { | |
| ret = errno; | |
| close(sock); | |
| sock = -1; | |
| errno = ret; | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| int ib_acm_connect(char *dest) | |
| { | |
| if (dest && *dest == '/') | |
| return ib_acm_connect_unix(dest); | |
| return ib_acm_connect_open(dest); | |
| } | |
| void ib_acm_disconnect(void) | |
| { | |
| if (sock != -1) { | |
| shutdown(sock, SHUT_RDWR); | |
| close(sock); | |
| sock = -1; | |
| } | |
| } | |
| static int acm_format_resp(struct acm_msg *msg, | |
| struct ibv_path_data **paths, int *count, int print) | |
| { | |
| struct ibv_path_data *path_data; | |
| char addr[ACM_MAX_ADDRESS]; | |
| int i, addr_cnt; | |
| *count = 0; | |
| addr_cnt = (msg->hdr.length - ACM_MSG_HDR_LENGTH) / | |
| sizeof(struct acm_ep_addr_data); | |
| path_data = (struct ibv_path_data *) | |
| calloc(1, addr_cnt * sizeof(struct ibv_path_data)); | |
| if (!path_data) | |
| return -1; | |
| for (i = 0; i < addr_cnt; i++) { | |
| switch (msg->resolve_data[i].type) { | |
| case ACM_EP_INFO_PATH: | |
| path_data[i].flags = msg->resolve_data[i].flags; | |
| path_data[i].path = msg->resolve_data[i].info.path; | |
| (*count)++; | |
| break; | |
| default: | |
| if (!(msg->resolve_data[i].flags & ACM_EP_FLAG_SOURCE)) | |
| goto err; | |
| switch (msg->resolve_data[i].type) { | |
| case ACM_EP_INFO_ADDRESS_IP: | |
| inet_ntop(AF_INET, msg->resolve_data[i].info.addr, | |
| addr, sizeof addr); | |
| break; | |
| case ACM_EP_INFO_ADDRESS_IP6: | |
| inet_ntop(AF_INET6, msg->resolve_data[i].info.addr, | |
| addr, sizeof addr); | |
| break; | |
| case ACM_EP_INFO_NAME: | |
| memcpy(addr, msg->resolve_data[i].info.name, | |
| ACM_MAX_ADDRESS); | |
| break; | |
| default: | |
| goto err; | |
| } | |
| if (print) | |
| printf("Source: %s\n", addr); | |
| break; | |
| } | |
| } | |
| *paths = path_data; | |
| return 0; | |
| err: | |
| free(path_data); | |
| return -1; | |
| } | |
| static int acm_format_ep_addr(struct acm_ep_addr_data *data, uint8_t *addr, | |
| uint8_t type, uint32_t flags) | |
| { | |
| data->type = type; | |
| data->flags = flags; | |
| switch (type) { | |
| case ACM_EP_INFO_NAME: | |
| if (!check_snprintf((char *)data->info.name, | |
| sizeof(data->info.name), "%s", (char *)addr)) | |
| return -1; | |
| break; | |
| case ACM_EP_INFO_ADDRESS_IP: | |
| memcpy(data->info.addr, &((struct sockaddr_in *) addr)->sin_addr, 4); | |
| break; | |
| case ACM_EP_INFO_ADDRESS_IP6: | |
| memcpy(data->info.addr, &((struct sockaddr_in6 *) addr)->sin6_addr, 16); | |
| break; | |
| default: | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| static inline int ERR(int err) | |
| { | |
| errno = err; | |
| return -1; | |
| } | |
| static int acm_error(uint8_t status) | |
| { | |
| switch (status) { | |
| case ACM_STATUS_SUCCESS: | |
| return 0; | |
| case ACM_STATUS_ENOMEM: | |
| return ERR(ENOMEM); | |
| case ACM_STATUS_EINVAL: | |
| return ERR(EINVAL); | |
| case ACM_STATUS_ENODATA: | |
| return ERR(ENODATA); | |
| case ACM_STATUS_ENOTCONN: | |
| return ERR(ENOTCONN); | |
| case ACM_STATUS_ETIMEDOUT: | |
| return ERR(ETIMEDOUT); | |
| case ACM_STATUS_ESRCADDR: | |
| case ACM_STATUS_EDESTADDR: | |
| return ERR(EADDRNOTAVAIL); | |
| case ACM_STATUS_ESRCTYPE: | |
| case ACM_STATUS_EDESTTYPE: | |
| default: | |
| return ERR(EINVAL); | |
| } | |
| } | |
| static int acm_resolve(uint8_t *src, uint8_t *dest, uint8_t type, | |
| struct ibv_path_data **paths, int *count, uint32_t flags, int print) | |
| { | |
| struct acm_msg msg; | |
| int ret, cnt = 0; | |
| pthread_mutex_lock(&acm_lock); | |
| memset(&msg, 0, sizeof msg); | |
| msg.hdr.version = ACM_VERSION; | |
| msg.hdr.opcode = ACM_OP_RESOLVE; | |
| if (src) { | |
| ret = acm_format_ep_addr(&msg.resolve_data[cnt++], src, type, | |
| ACM_EP_FLAG_SOURCE); | |
| if (ret) | |
| goto out; | |
| } | |
| ret = acm_format_ep_addr(&msg.resolve_data[cnt++], dest, type, | |
| ACM_EP_FLAG_DEST | flags); | |
| if (ret) | |
| goto out; | |
| msg.hdr.length = ACM_MSG_HDR_LENGTH + (cnt * ACM_MSG_EP_LENGTH); | |
| ret = send(sock, (char *) &msg, msg.hdr.length, 0); | |
| if (ret != msg.hdr.length) | |
| goto out; | |
| ret = recv(sock, (char *) &msg, sizeof msg, 0); | |
| if (ret < ACM_MSG_HDR_LENGTH || ret != msg.hdr.length) | |
| goto out; | |
| if (msg.hdr.status) { | |
| ret = acm_error(msg.hdr.status); | |
| goto out; | |
| } | |
| ret = acm_format_resp(&msg, paths, count, print); | |
| out: | |
| pthread_mutex_unlock(&acm_lock); | |
| return ret; | |
| } | |
| int ib_acm_resolve_name(char *src, char *dest, | |
| struct ibv_path_data **paths, int *count, uint32_t flags, int print) | |
| { | |
| return acm_resolve((uint8_t *) src, (uint8_t *) dest, | |
| ACM_EP_INFO_NAME, paths, count, flags, print); | |
| } | |
| int ib_acm_resolve_ip(struct sockaddr *src, struct sockaddr *dest, | |
| struct ibv_path_data **paths, int *count, uint32_t flags, int print) | |
| { | |
| if (((struct sockaddr *) dest)->sa_family == AF_INET) { | |
| return acm_resolve((uint8_t *) src, (uint8_t *) dest, | |
| ACM_EP_INFO_ADDRESS_IP, paths, count, flags, print); | |
| } else { | |
| return acm_resolve((uint8_t *) src, (uint8_t *) dest, | |
| ACM_EP_INFO_ADDRESS_IP6, paths, count, flags, print); | |
| } | |
| } | |
| int ib_acm_resolve_path(struct ibv_path_record *path, uint32_t flags) | |
| { | |
| struct acm_msg msg; | |
| struct acm_ep_addr_data *data; | |
| int ret; | |
| pthread_mutex_lock(&acm_lock); | |
| memset(&msg, 0, sizeof msg); | |
| msg.hdr.version = ACM_VERSION; | |
| msg.hdr.opcode = ACM_OP_RESOLVE; | |
| msg.hdr.length = ACM_MSG_HDR_LENGTH + ACM_MSG_EP_LENGTH; | |
| data = &msg.resolve_data[0]; | |
| data->flags = flags; | |
| data->type = ACM_EP_INFO_PATH; | |
| data->info.path = *path; | |
| ret = send(sock, (char *) &msg, msg.hdr.length, 0); | |
| if (ret != msg.hdr.length) | |
| goto out; | |
| ret = recv(sock, (char *) &msg, sizeof msg, 0); | |
| if (ret < ACM_MSG_HDR_LENGTH || ret != msg.hdr.length) | |
| goto out; | |
| ret = acm_error(msg.hdr.status); | |
| if (!ret) | |
| *path = data->info.path; | |
| out: | |
| pthread_mutex_unlock(&acm_lock); | |
| return ret; | |
| } | |
| int ib_acm_query_perf(int index, uint64_t **counters, int *count) | |
| { | |
| struct acm_msg msg; | |
| int ret, i; | |
| pthread_mutex_lock(&acm_lock); | |
| memset(&msg, 0, sizeof msg); | |
| msg.hdr.version = ACM_VERSION; | |
| msg.hdr.opcode = ACM_OP_PERF_QUERY; | |
| msg.hdr.src_index = index; | |
| msg.hdr.length = htobe16(ACM_MSG_HDR_LENGTH); | |
| ret = send(sock, (char *) &msg, ACM_MSG_HDR_LENGTH, 0); | |
| if (ret != ACM_MSG_HDR_LENGTH) | |
| goto out; | |
| ret = recv(sock, (char *) &msg, sizeof msg, 0); | |
| if (ret < ACM_MSG_HDR_LENGTH || ret != be16toh(msg.hdr.length)) { | |
| ret = ACM_STATUS_EINVAL; | |
| goto out; | |
| } | |
| if (msg.hdr.status) { | |
| ret = acm_error(msg.hdr.status); | |
| goto out; | |
| } | |
| *counters = malloc(sizeof(uint64_t) * msg.hdr.src_out); | |
| if (!*counters) { | |
| ret = ACM_STATUS_ENOMEM; | |
| goto out; | |
| } | |
| *count = msg.hdr.src_out; | |
| for (i = 0; i < *count; i++) | |
| (*counters)[i] = be64toh(msg.perf_data[i]); | |
| ret = 0; | |
| out: | |
| pthread_mutex_unlock(&acm_lock); | |
| return ret; | |
| } | |
| int ib_acm_enum_ep(int index, struct acm_ep_config_data **data, uint8_t port) | |
| { | |
| struct acm_ep_config_data *netw_edata = NULL; | |
| struct acm_ep_config_data *host_edata = NULL; | |
| struct acm_hdr hdr; | |
| struct acm_msg msg; | |
| int ret; | |
| int len; | |
| int i; | |
| pthread_mutex_lock(&acm_lock); | |
| memset(&msg, 0, sizeof msg); | |
| msg.hdr.version = ACM_VERSION; | |
| msg.hdr.opcode = ACM_OP_EP_QUERY; | |
| msg.hdr.src_out = index; | |
| msg.hdr.src_index = port; | |
| msg.hdr.length = htobe16(ACM_MSG_HDR_LENGTH); | |
| ret = send(sock, (char *) &msg, ACM_MSG_HDR_LENGTH, 0); | |
| if (ret != ACM_MSG_HDR_LENGTH) | |
| goto out; | |
| ret = recv(sock, (char *) &hdr, sizeof(hdr), 0); | |
| if (ret != sizeof(hdr)) { | |
| ret = ACM_STATUS_EINVAL; | |
| goto out; | |
| } | |
| if (hdr.status) { | |
| ret = acm_error(hdr.status); | |
| goto out; | |
| } | |
| len = be16toh(hdr.length) - sizeof(hdr); | |
| netw_edata = malloc(len); | |
| host_edata = malloc(len); | |
| if (!netw_edata || !host_edata) { | |
| ret = ACM_STATUS_ENOMEM; | |
| goto out; | |
| } | |
| ret = recv(sock, (char *)netw_edata, len, 0); | |
| if (ret != len) { | |
| ret = ACM_STATUS_EINVAL; | |
| goto out; | |
| } | |
| host_edata->dev_guid = be64toh(netw_edata->dev_guid); | |
| host_edata->port_num = netw_edata->port_num; | |
| host_edata->phys_port_cnt = netw_edata->phys_port_cnt; | |
| host_edata->pkey = be16toh(netw_edata->pkey); | |
| host_edata->addr_cnt = be16toh(netw_edata->addr_cnt); | |
| memcpy(host_edata->prov_name, netw_edata->prov_name, | |
| sizeof(host_edata->prov_name)); | |
| for (i = 0; i < host_edata->addr_cnt; ++i) | |
| host_edata->addrs[i] = netw_edata->addrs[i]; | |
| *data = host_edata; | |
| ret = 0; | |
| out: | |
| free(netw_edata); | |
| if (ret) | |
| free(host_edata); | |
| pthread_mutex_unlock(&acm_lock); | |
| return ret; | |
| } | |
| int ib_acm_query_perf_ep_addr(uint8_t *src, uint8_t type, | |
| uint64_t **counters, int *count) | |
| { | |
| struct acm_msg msg; | |
| int ret, i, len; | |
| if (!src) | |
| return -1; | |
| pthread_mutex_lock(&acm_lock); | |
| memset(&msg, 0, sizeof msg); | |
| msg.hdr.version = ACM_VERSION; | |
| msg.hdr.opcode = ACM_OP_PERF_QUERY; | |
| ret = acm_format_ep_addr(&msg.resolve_data[0], src, type, | |
| ACM_EP_FLAG_SOURCE); | |
| if (ret) | |
| goto out; | |
| len = ACM_MSG_HDR_LENGTH + ACM_MSG_EP_LENGTH; | |
| msg.hdr.length = htobe16(len); | |
| ret = send(sock, (char *) &msg, len, 0); | |
| if (ret != len) | |
| goto out; | |
| ret = recv(sock, (char *) &msg, sizeof msg, 0); | |
| if (ret < ACM_MSG_HDR_LENGTH || ret != be16toh(msg.hdr.length)) { | |
| ret = ACM_STATUS_EINVAL; | |
| goto out; | |
| } | |
| if (msg.hdr.status) { | |
| ret = acm_error(msg.hdr.status); | |
| goto out; | |
| } | |
| *counters = malloc(sizeof(uint64_t) * msg.hdr.src_out); | |
| if (!*counters) { | |
| ret = ACM_STATUS_ENOMEM; | |
| goto out; | |
| } | |
| *count = msg.hdr.src_out; | |
| for (i = 0; i < *count; i++) | |
| (*counters)[i] = be64toh(msg.perf_data[i]); | |
| ret = 0; | |
| out: | |
| pthread_mutex_unlock(&acm_lock); | |
| return ret; | |
| } | |
| const char *ib_acm_cntr_name(int index) | |
| { | |
| static const char *const cntr_name[] = { | |
| [ACM_CNTR_ERROR] = "Error Count", | |
| [ACM_CNTR_RESOLVE] = "Resolve Count", | |
| [ACM_CNTR_NODATA] = "No Data", | |
| [ACM_CNTR_ADDR_QUERY] = "Addr Query Count", | |
| [ACM_CNTR_ADDR_CACHE] = "Addr Cache Count", | |
| [ACM_CNTR_ROUTE_QUERY] = "Route Query Count", | |
| [ACM_CNTR_ROUTE_CACHE] = "Route Cache Count", | |
| }; | |
| if (index < ACM_CNTR_ERROR || index > ACM_MAX_COUNTER) | |
| return "Unknown"; | |
| return cntr_name[index]; | |
| } |