Blame libibmad/src/rpc.c

Packit db064d
/*
Packit db064d
 * Copyright (c) 2004-2009 Voltaire Inc.  All rights reserved.
Packit db064d
 * Copyright (c) 2009 HNR Consulting.  All rights reserved.
Packit db064d
 * Copyright (c) 2011 Mellanox Technologies LTD.  All rights reserved.
Packit db064d
 *
Packit db064d
 * This software is available to you under a choice of one of two
Packit db064d
 * licenses.  You may choose to be licensed under the terms of the GNU
Packit db064d
 * General Public License (GPL) Version 2, available from the file
Packit db064d
 * COPYING in the main directory of this source tree, or the
Packit db064d
 * OpenIB.org BSD license below:
Packit db064d
 *
Packit db064d
 *     Redistribution and use in source and binary forms, with or
Packit db064d
 *     without modification, are permitted provided that the following
Packit db064d
 *     conditions are met:
Packit db064d
 *
Packit db064d
 *      - Redistributions of source code must retain the above
Packit db064d
 *        copyright notice, this list of conditions and the following
Packit db064d
 *        disclaimer.
Packit db064d
 *
Packit db064d
 *      - Redistributions in binary form must reproduce the above
Packit db064d
 *        copyright notice, this list of conditions and the following
Packit db064d
 *        disclaimer in the documentation and/or other materials
Packit db064d
 *        provided with the distribution.
Packit db064d
 *
Packit db064d
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Packit db064d
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Packit db064d
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Packit db064d
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
Packit db064d
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
Packit db064d
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
Packit db064d
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
Packit db064d
 * SOFTWARE.
Packit db064d
 *
Packit db064d
 */
Packit db064d
Packit db064d
#if HAVE_CONFIG_H
Packit db064d
#  include <config.h>
Packit db064d
#endif				/* HAVE_CONFIG_H */
Packit db064d
Packit db064d
#include <stdio.h>
Packit db064d
#include <stdlib.h>
Packit db064d
#include <string.h>
Packit db064d
#include <errno.h>
Packit db064d
Packit db064d
#include <infiniband/umad.h>
Packit db064d
#include <infiniband/mad.h>
Packit db064d
Packit db064d
#include "mad_internal.h"
Packit db064d
Packit db064d
int ibdebug;
Packit db064d
Packit db064d
static struct ibmad_port mad_port;
Packit db064d
struct ibmad_port *ibmp = &mad_port;
Packit db064d
Packit db064d
static int iberrs;
Packit db064d
Packit db064d
int madrpc_retries = MAD_DEF_RETRIES;
Packit db064d
int madrpc_timeout = MAD_DEF_TIMEOUT_MS;
Packit db064d
Packit db064d
static void *save_mad;
Packit db064d
static int save_mad_len = 256;
Packit db064d
Packit db064d
#undef DEBUG
Packit db064d
#define DEBUG	if (ibdebug)	IBWARN
Packit db064d
#define ERRS(fmt, ...) do {	\
Packit db064d
	if (iberrs || ibdebug)	\
Packit db064d
		IBWARN(fmt, ## __VA_ARGS__); \
Packit db064d
} while (0)
Packit db064d
Packit db064d
#define MAD_TID(mad)	(*((uint64_t *)((char *)(mad) + 8)))
Packit db064d
Packit db064d
void madrpc_show_errors(int set)
Packit db064d
{
Packit db064d
	iberrs = set;
Packit db064d
}
Packit db064d
Packit db064d
void madrpc_save_mad(void *madbuf, int len)
Packit db064d
{
Packit db064d
	save_mad = madbuf;
Packit db064d
	save_mad_len = len;
Packit db064d
}
Packit db064d
Packit db064d
int madrpc_set_retries(int retries)
Packit db064d
{
Packit db064d
	if (retries > 0)
Packit db064d
		madrpc_retries = retries;
Packit db064d
	return madrpc_retries;
Packit db064d
}
Packit db064d
Packit db064d
int madrpc_set_timeout(int timeout)
Packit db064d
{
Packit db064d
	madrpc_timeout = timeout;
Packit db064d
	return 0;
Packit db064d
}
Packit db064d
Packit db064d
void mad_rpc_set_retries(struct ibmad_port *port, int retries)
Packit db064d
{
Packit db064d
	port->retries = retries;
Packit db064d
}
Packit db064d
Packit db064d
void mad_rpc_set_timeout(struct ibmad_port *port, int timeout)
Packit db064d
{
Packit db064d
	port->timeout = timeout;
Packit db064d
}
Packit db064d
Packit db064d
int madrpc_portid(void)
Packit db064d
{
Packit db064d
	return ibmp->port_id;
Packit db064d
}
Packit db064d
Packit db064d
int mad_rpc_portid(struct ibmad_port *srcport)
Packit db064d
{
Packit db064d
	return srcport->port_id;
Packit db064d
}
Packit db064d
Packit db064d
int mad_rpc_class_agent(struct ibmad_port *port, int class)
Packit db064d
{
Packit db064d
	if (class < 1 || class >= MAX_CLASS)
Packit db064d
		return -1;
Packit db064d
	return port->class_agents[class];
Packit db064d
}
Packit db064d
Packit db064d
static int
Packit db064d
_do_madrpc(int port_id, void *sndbuf, void *rcvbuf, int agentid, int len,
Packit db064d
	   int timeout, int max_retries, int *p_error)
Packit db064d
{
Packit db064d
	uint32_t trid;		/* only low 32 bits - see mad_trid() */
Packit db064d
	int retries;
Packit db064d
	int length, status;
Packit db064d
Packit db064d
	if (ibdebug > 1) {
Packit db064d
		IBWARN(">>> sending: len %d pktsz %zu", len, umad_size() + len);
Packit db064d
		xdump(stderr, "send buf\n", sndbuf, umad_size() + len);
Packit db064d
	}
Packit db064d
Packit db064d
	if (save_mad) {
Packit db064d
		memcpy(save_mad, umad_get_mad(sndbuf),
Packit db064d
		       save_mad_len < len ? save_mad_len : len);
Packit db064d
		save_mad = NULL;
Packit db064d
	}
Packit db064d
Packit db064d
	if (max_retries <= 0) {
Packit db064d
		errno = EINVAL;
Packit db064d
		*p_error = EINVAL;
Packit db064d
		ERRS("max_retries %d <= 0", max_retries);
Packit db064d
		return -1;
Packit db064d
	}
Packit db064d
Packit db064d
	trid =
Packit db064d
	    (uint32_t) mad_get_field64(umad_get_mad(sndbuf), 0, IB_MAD_TRID_F);
Packit db064d
Packit db064d
	for (retries = 0; retries < max_retries; retries++) {
Packit db064d
		if (retries)
Packit db064d
			ERRS("retry %d (timeout %d ms)", retries, timeout);
Packit db064d
Packit db064d
		length = len;
Packit db064d
		if (umad_send(port_id, agentid, sndbuf, length, timeout, 0) < 0) {
Packit db064d
			IBWARN("send failed; %s", strerror(errno));
Packit db064d
			return -1;
Packit db064d
		}
Packit db064d
Packit db064d
		/* Use same timeout on receive side just in case */
Packit db064d
		/* send packet is lost somewhere. */
Packit db064d
		do {
Packit db064d
			length = len;
Packit db064d
			if (umad_recv(port_id, rcvbuf, &length, timeout) < 0) {
Packit db064d
				IBWARN("recv failed: %s", strerror(errno));
Packit db064d
				return -1;
Packit db064d
			}
Packit db064d
Packit db064d
			if (ibdebug > 2)
Packit db064d
				umad_addr_dump(umad_get_mad_addr(rcvbuf));
Packit db064d
			if (ibdebug > 1) {
Packit db064d
				IBWARN("rcv buf:");
Packit db064d
				xdump(stderr, "rcv buf\n", umad_get_mad(rcvbuf),
Packit db064d
				      IB_MAD_SIZE);
Packit db064d
			}
Packit db064d
		} while ((uint32_t)
Packit db064d
			 mad_get_field64(umad_get_mad(rcvbuf), 0,
Packit db064d
					 IB_MAD_TRID_F) != trid);
Packit db064d
Packit db064d
		status = umad_status(rcvbuf);
Packit db064d
		if (!status)
Packit db064d
			return length;	/* done */
Packit db064d
		if (status == ENOMEM)
Packit db064d
			return length;
Packit db064d
	}
Packit db064d
Packit db064d
	errno = status;
Packit db064d
	*p_error = ETIMEDOUT;
Packit db064d
	ERRS("timeout after %d retries, %d ms", retries, timeout * retries);
Packit db064d
	return -1;
Packit db064d
}
Packit db064d
Packit db064d
static int redirect_port(ib_portid_t * port, uint8_t * mad)
Packit db064d
{
Packit db064d
	port->lid = mad_get_field(mad, 64, IB_CPI_REDIRECT_LID_F);
Packit db064d
	if (!port->lid) {
Packit db064d
		IBWARN("GID-based redirection is not supported");
Packit db064d
		return -1;
Packit db064d
	}
Packit db064d
Packit db064d
	port->qp = mad_get_field(mad, 64, IB_CPI_REDIRECT_QP_F);
Packit db064d
	port->qkey = mad_get_field(mad, 64, IB_CPI_REDIRECT_QKEY_F);
Packit db064d
	port->sl = (uint8_t) mad_get_field(mad, 64, IB_CPI_REDIRECT_SL_F);
Packit db064d
Packit db064d
	/* TODO: Reverse map redirection P_Key to P_Key index */
Packit db064d
Packit db064d
	if (ibdebug)
Packit db064d
		IBWARN("redirected to lid %d, qp 0x%x, qkey 0x%x, sl 0x%x",
Packit db064d
		       port->lid, port->qp, port->qkey, port->sl);
Packit db064d
Packit db064d
	return 0;
Packit db064d
}
Packit db064d
Packit db064d
void *mad_rpc(const struct ibmad_port *port, ib_rpc_t * rpc,
Packit db064d
	      ib_portid_t * dport, void *payload, void *rcvdata)
Packit db064d
{
Packit db064d
	int status, len;
Packit db064d
	uint8_t sndbuf[1024], rcvbuf[1024], *mad;
Packit db064d
	ib_rpc_v1_t *rpcv1 = (ib_rpc_v1_t *)rpc;
Packit db064d
	int error = 0;
Packit db064d
Packit db064d
	if ((rpc->mgtclass & IB_MAD_RPC_VERSION_MASK) == IB_MAD_RPC_VERSION1)
Packit db064d
		rpcv1->error = 0;
Packit db064d
	do {
Packit db064d
		len = 0;
Packit db064d
		memset(sndbuf, 0, umad_size() + IB_MAD_SIZE);
Packit db064d
Packit db064d
		if ((len = mad_build_pkt(sndbuf, rpc, dport, NULL, payload)) < 0)
Packit db064d
			return NULL;
Packit db064d
Packit db064d
		if ((len = _do_madrpc(port->port_id, sndbuf, rcvbuf,
Packit db064d
				      port->class_agents[rpc->mgtclass & 0xff],
Packit db064d
				      len, mad_get_timeout(port, rpc->timeout),
Packit db064d
				      mad_get_retries(port), &error)) < 0) {
Packit db064d
			if ((rpc->mgtclass & IB_MAD_RPC_VERSION_MASK) ==
Packit db064d
			    IB_MAD_RPC_VERSION1)
Packit db064d
				rpcv1->error = error;
Packit db064d
			IBWARN("_do_madrpc failed; dport (%s)",
Packit db064d
			       portid2str(dport));
Packit db064d
			return NULL;
Packit db064d
		}
Packit db064d
Packit db064d
		mad = umad_get_mad(rcvbuf);
Packit db064d
		status = mad_get_field(mad, 0, IB_DRSMP_STATUS_F);
Packit db064d
Packit db064d
		/* check for exact match instead of only the redirect bit;
Packit db064d
		 * that way, weird statuses cause an error, too */
Packit db064d
		if (status == IB_MAD_STS_REDIRECT) {
Packit db064d
			/* update dport for next request and retry */
Packit db064d
			/* bail if redirection fails */
Packit db064d
			if (redirect_port(dport, mad))
Packit db064d
				break;
Packit db064d
		} else
Packit db064d
			break;
Packit db064d
	} while (1);
Packit db064d
Packit db064d
	if ((rpc->mgtclass & IB_MAD_RPC_VERSION_MASK) == IB_MAD_RPC_VERSION1)
Packit db064d
		rpcv1->error = error;
Packit db064d
	rpc->rstatus = status;
Packit db064d
Packit db064d
	if (status != 0) {
Packit db064d
		ERRS("MAD completed with error status 0x%x; dport (%s)",
Packit db064d
		     status, portid2str(dport));
Packit db064d
		errno = EIO;
Packit db064d
		return NULL;
Packit db064d
	}
Packit db064d
Packit db064d
	if (rcvdata)
Packit db064d
		memcpy(rcvdata, mad + rpc->dataoffs, rpc->datasz);
Packit db064d
Packit db064d
	return rcvdata;
Packit db064d
}
Packit db064d
Packit db064d
void *mad_rpc_rmpp(const struct ibmad_port *port, ib_rpc_t * rpc,
Packit db064d
		   ib_portid_t * dport, ib_rmpp_hdr_t * rmpp, void *data)
Packit db064d
{
Packit db064d
	int status, len;
Packit db064d
	uint8_t sndbuf[1024], rcvbuf[1024], *mad;
Packit db064d
	ib_rpc_v1_t *rpcv1 = (ib_rpc_v1_t *)rpc;
Packit db064d
	int error = 0;
Packit db064d
Packit db064d
	memset(sndbuf, 0, umad_size() + IB_MAD_SIZE);
Packit db064d
Packit db064d
	DEBUG("rmpp %p data %p", rmpp, data);
Packit db064d
Packit db064d
	if ((rpc->mgtclass & IB_MAD_RPC_VERSION_MASK) == IB_MAD_RPC_VERSION1)
Packit db064d
		rpcv1->error = 0;
Packit db064d
	if ((len = mad_build_pkt(sndbuf, rpc, dport, rmpp, data)) < 0)
Packit db064d
		return NULL;
Packit db064d
Packit db064d
	if ((len = _do_madrpc(port->port_id, sndbuf, rcvbuf,
Packit db064d
			      port->class_agents[rpc->mgtclass & 0xff],
Packit db064d
			      len, mad_get_timeout(port, rpc->timeout),
Packit db064d
			      mad_get_retries(port), &error)) < 0) {
Packit db064d
		if ((rpc->mgtclass & IB_MAD_RPC_VERSION_MASK) == IB_MAD_RPC_VERSION1)
Packit db064d
			rpcv1->error = error;
Packit db064d
		IBWARN("_do_madrpc failed; dport (%s)", portid2str(dport));
Packit db064d
		return NULL;
Packit db064d
	}
Packit db064d
Packit db064d
	if ((rpc->mgtclass & IB_MAD_RPC_VERSION_MASK) == IB_MAD_RPC_VERSION1)
Packit db064d
		rpcv1->error = error;
Packit db064d
Packit db064d
	mad = umad_get_mad(rcvbuf);
Packit db064d
Packit db064d
	if ((status = mad_get_field(mad, 0, IB_MAD_STATUS_F)) != 0) {
Packit db064d
		ERRS("MAD completed with error status 0x%x; dport (%s)",
Packit db064d
		     status, portid2str(dport));
Packit db064d
		errno = EIO;
Packit db064d
		return NULL;
Packit db064d
	}
Packit db064d
Packit db064d
	if (rmpp) {
Packit db064d
		rmpp->flags = mad_get_field(mad, 0, IB_SA_RMPP_FLAGS_F);
Packit db064d
		if ((rmpp->flags & 0x3) &&
Packit db064d
		    mad_get_field(mad, 0, IB_SA_RMPP_VERS_F) != 1) {
Packit db064d
			IBWARN("bad rmpp version");
Packit db064d
			return NULL;
Packit db064d
		}
Packit db064d
		rmpp->type = mad_get_field(mad, 0, IB_SA_RMPP_TYPE_F);
Packit db064d
		rmpp->status = mad_get_field(mad, 0, IB_SA_RMPP_STATUS_F);
Packit db064d
		DEBUG("rmpp type %d status %d", rmpp->type, rmpp->status);
Packit db064d
		rmpp->d1.u = mad_get_field(mad, 0, IB_SA_RMPP_D1_F);
Packit db064d
		rmpp->d2.u = mad_get_field(mad, 0, IB_SA_RMPP_D2_F);
Packit db064d
	}
Packit db064d
Packit db064d
	if (data)
Packit db064d
		memcpy(data, mad + rpc->dataoffs, rpc->datasz);
Packit db064d
Packit db064d
	rpc->recsz = mad_get_field(mad, 0, IB_SA_ATTROFFS_F);
Packit db064d
Packit db064d
	return data;
Packit db064d
}
Packit db064d
Packit db064d
void *madrpc(ib_rpc_t * rpc, ib_portid_t * dport, void *payload, void *rcvdata)
Packit db064d
{
Packit db064d
	return mad_rpc(ibmp, rpc, dport, payload, rcvdata);
Packit db064d
}
Packit db064d
Packit db064d
void *madrpc_rmpp(ib_rpc_t * rpc, ib_portid_t * dport, ib_rmpp_hdr_t * rmpp,
Packit db064d
		  void *data)
Packit db064d
{
Packit db064d
	return mad_rpc_rmpp(ibmp, rpc, dport, rmpp, data);
Packit db064d
}
Packit db064d
Packit db064d
void
Packit db064d
madrpc_init(char *dev_name, int dev_port, int *mgmt_classes, int num_classes)
Packit db064d
{
Packit db064d
	int fd;
Packit db064d
Packit db064d
	if (umad_init() < 0)
Packit db064d
		IBPANIC("can't init UMAD library");
Packit db064d
Packit db064d
	if ((fd = umad_open_port(dev_name, dev_port)) < 0)
Packit db064d
		IBPANIC("can't open UMAD port (%s:%d)",
Packit db064d
		dev_name ? dev_name : "(nil)", dev_port);
Packit db064d
Packit db064d
	if (num_classes >= MAX_CLASS)
Packit db064d
		IBPANIC("too many classes %d requested", num_classes);
Packit db064d
Packit db064d
	ibmp->port_id = fd;
Packit db064d
	memset(ibmp->class_agents, 0xff, sizeof ibmp->class_agents);
Packit db064d
	while (num_classes--) {
Packit db064d
		uint8_t rmpp_version = 0;
Packit db064d
		int mgmt = *mgmt_classes++;
Packit db064d
Packit db064d
		if (mgmt == IB_SA_CLASS)
Packit db064d
			rmpp_version = 1;
Packit db064d
		if (mad_register_client_via(mgmt, rmpp_version, ibmp) < 0)
Packit db064d
			IBPANIC("client_register for mgmt class %d failed",
Packit db064d
				mgmt);
Packit db064d
	}
Packit db064d
}
Packit db064d
Packit db064d
struct ibmad_port *mad_rpc_open_port(char *dev_name, int dev_port,
Packit db064d
				     int *mgmt_classes, int num_classes)
Packit db064d
{
Packit db064d
	struct ibmad_port *p;
Packit db064d
	int port_id;
Packit db064d
Packit db064d
	if (num_classes >= MAX_CLASS) {
Packit db064d
		IBWARN("too many classes %d requested", num_classes);
Packit db064d
		errno = EINVAL;
Packit db064d
		return NULL;
Packit db064d
	}
Packit db064d
Packit db064d
	if (umad_init() < 0) {
Packit db064d
		IBWARN("can't init UMAD library");
Packit db064d
		errno = ENODEV;
Packit db064d
		return NULL;
Packit db064d
	}
Packit db064d
Packit db064d
	p = malloc(sizeof(*p));
Packit db064d
	if (!p) {
Packit db064d
		errno = ENOMEM;
Packit db064d
		return NULL;
Packit db064d
	}
Packit db064d
	memset(p, 0, sizeof(*p));
Packit db064d
Packit db064d
	if ((port_id = umad_open_port(dev_name, dev_port)) < 0) {
Packit db064d
		IBWARN("can't open UMAD port (%s:%d)", dev_name, dev_port);
Packit db064d
		if (!errno)
Packit db064d
			errno = EIO;
Packit db064d
		free(p);
Packit db064d
		return NULL;
Packit db064d
	}
Packit db064d
Packit db064d
	p->port_id = port_id;
Packit db064d
	memset(p->class_agents, 0xff, sizeof p->class_agents);
Packit db064d
	while (num_classes--) {
Packit db064d
		uint8_t rmpp_version = 0;
Packit db064d
		int mgmt = *mgmt_classes++;
Packit db064d
Packit db064d
		if (mgmt == IB_SA_CLASS)
Packit db064d
			rmpp_version = 1;
Packit db064d
		if (mgmt < 0 || mgmt >= MAX_CLASS ||
Packit db064d
		    mad_register_client_via(mgmt, rmpp_version, p) < 0) {
Packit db064d
			IBWARN("client_register for mgmt %d failed", mgmt);
Packit db064d
			if (!errno)
Packit db064d
				errno = EINVAL;
Packit db064d
			umad_close_port(port_id);
Packit db064d
			free(p);
Packit db064d
			return NULL;
Packit db064d
		}
Packit db064d
	}
Packit db064d
Packit db064d
	return p;
Packit db064d
}
Packit db064d
Packit db064d
void mad_rpc_close_port(struct ibmad_port *port)
Packit db064d
{
Packit db064d
	umad_close_port(port->port_id);
Packit db064d
	free(port);
Packit db064d
}