blob: 74adbf4683e33fcaafd46814fc96cdd529c0ec0c [file] [log] [blame]
/*
* 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];
}