| /* |
| * Copyright (c) 2004-2009 Voltaire Inc. All rights reserved. |
| * Copyright (c) 2009 HNR Consulting. All rights reserved. |
| * Copyright (c) 2011 Mellanox Technologies LTD. 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 <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #include <infiniband/umad.h> |
| #include <infiniband/mad.h> |
| |
| #include "mad_internal.h" |
| |
| int ibdebug; |
| |
| static struct ibmad_port mad_port; |
| struct ibmad_port *ibmp = &mad_port; |
| |
| static int iberrs; |
| |
| int madrpc_retries = MAD_DEF_RETRIES; |
| int madrpc_timeout = MAD_DEF_TIMEOUT_MS; |
| |
| static void *save_mad; |
| static int save_mad_len = 256; |
| |
| #undef DEBUG |
| #define DEBUG if (ibdebug) IBWARN |
| #define ERRS(fmt, ...) do { \ |
| if (iberrs || ibdebug) \ |
| IBWARN(fmt, ## __VA_ARGS__); \ |
| } while (0) |
| |
| #define MAD_TID(mad) (*((uint64_t *)((char *)(mad) + 8))) |
| |
| void madrpc_show_errors(int set) |
| { |
| iberrs = set; |
| } |
| |
| void madrpc_save_mad(void *madbuf, int len) |
| { |
| save_mad = madbuf; |
| save_mad_len = len; |
| } |
| |
| int madrpc_set_retries(int retries) |
| { |
| if (retries > 0) |
| madrpc_retries = retries; |
| return madrpc_retries; |
| } |
| |
| int madrpc_set_timeout(int timeout) |
| { |
| madrpc_timeout = timeout; |
| return 0; |
| } |
| |
| void mad_rpc_set_retries(struct ibmad_port *port, int retries) |
| { |
| port->retries = retries; |
| } |
| |
| void mad_rpc_set_timeout(struct ibmad_port *port, int timeout) |
| { |
| port->timeout = timeout; |
| } |
| |
| int madrpc_portid(void) |
| { |
| return ibmp->port_id; |
| } |
| |
| int mad_rpc_portid(struct ibmad_port *srcport) |
| { |
| return srcport->port_id; |
| } |
| |
| int mad_rpc_class_agent(struct ibmad_port *port, int class) |
| { |
| if (class < 1 || class >= MAX_CLASS) |
| return -1; |
| return port->class_agents[class]; |
| } |
| |
| static int |
| _do_madrpc(int port_id, void *sndbuf, void *rcvbuf, int agentid, int len, |
| int timeout, int max_retries, int *p_error) |
| { |
| uint32_t trid; /* only low 32 bits - see mad_trid() */ |
| int retries; |
| int length, status; |
| |
| if (ibdebug > 1) { |
| IBWARN(">>> sending: len %d pktsz %zu", len, umad_size() + len); |
| xdump(stderr, "send buf\n", sndbuf, umad_size() + len); |
| } |
| |
| if (save_mad) { |
| memcpy(save_mad, umad_get_mad(sndbuf), |
| save_mad_len < len ? save_mad_len : len); |
| save_mad = NULL; |
| } |
| |
| if (max_retries <= 0) { |
| errno = EINVAL; |
| *p_error = EINVAL; |
| ERRS("max_retries %d <= 0", max_retries); |
| return -1; |
| } |
| |
| trid = |
| (uint32_t) mad_get_field64(umad_get_mad(sndbuf), 0, IB_MAD_TRID_F); |
| |
| for (retries = 0; retries < max_retries; retries++) { |
| if (retries) |
| ERRS("retry %d (timeout %d ms)", retries, timeout); |
| |
| length = len; |
| if (umad_send(port_id, agentid, sndbuf, length, timeout, 0) < 0) { |
| IBWARN("send failed; %s", strerror(errno)); |
| return -1; |
| } |
| |
| /* Use same timeout on receive side just in case */ |
| /* send packet is lost somewhere. */ |
| do { |
| length = len; |
| if (umad_recv(port_id, rcvbuf, &length, timeout) < 0) { |
| IBWARN("recv failed: %s", strerror(errno)); |
| return -1; |
| } |
| |
| if (ibdebug > 2) |
| umad_addr_dump(umad_get_mad_addr(rcvbuf)); |
| if (ibdebug > 1) { |
| IBWARN("rcv buf:"); |
| xdump(stderr, "rcv buf\n", umad_get_mad(rcvbuf), |
| IB_MAD_SIZE); |
| } |
| } while ((uint32_t) |
| mad_get_field64(umad_get_mad(rcvbuf), 0, |
| IB_MAD_TRID_F) != trid); |
| |
| status = umad_status(rcvbuf); |
| if (!status) |
| return length; /* done */ |
| if (status == ENOMEM) |
| return length; |
| } |
| |
| errno = status; |
| *p_error = ETIMEDOUT; |
| ERRS("timeout after %d retries, %d ms", retries, timeout * retries); |
| return -1; |
| } |
| |
| static int redirect_port(ib_portid_t * port, uint8_t * mad) |
| { |
| port->lid = mad_get_field(mad, 64, IB_CPI_REDIRECT_LID_F); |
| if (!port->lid) { |
| IBWARN("GID-based redirection is not supported"); |
| return -1; |
| } |
| |
| port->qp = mad_get_field(mad, 64, IB_CPI_REDIRECT_QP_F); |
| port->qkey = mad_get_field(mad, 64, IB_CPI_REDIRECT_QKEY_F); |
| port->sl = (uint8_t) mad_get_field(mad, 64, IB_CPI_REDIRECT_SL_F); |
| |
| /* TODO: Reverse map redirection P_Key to P_Key index */ |
| |
| if (ibdebug) |
| IBWARN("redirected to lid %d, qp 0x%x, qkey 0x%x, sl 0x%x", |
| port->lid, port->qp, port->qkey, port->sl); |
| |
| return 0; |
| } |
| |
| void *mad_rpc(const struct ibmad_port *port, ib_rpc_t * rpc, |
| ib_portid_t * dport, void *payload, void *rcvdata) |
| { |
| int status, len; |
| uint8_t sndbuf[1024], rcvbuf[1024], *mad; |
| ib_rpc_v1_t *rpcv1 = (ib_rpc_v1_t *)rpc; |
| int error = 0; |
| |
| if ((rpc->mgtclass & IB_MAD_RPC_VERSION_MASK) == IB_MAD_RPC_VERSION1) |
| rpcv1->error = 0; |
| do { |
| len = 0; |
| memset(sndbuf, 0, umad_size() + IB_MAD_SIZE); |
| |
| if ((len = mad_build_pkt(sndbuf, rpc, dport, NULL, payload)) < 0) |
| return NULL; |
| |
| if ((len = _do_madrpc(port->port_id, sndbuf, rcvbuf, |
| port->class_agents[rpc->mgtclass & 0xff], |
| len, mad_get_timeout(port, rpc->timeout), |
| mad_get_retries(port), &error)) < 0) { |
| if ((rpc->mgtclass & IB_MAD_RPC_VERSION_MASK) == |
| IB_MAD_RPC_VERSION1) |
| rpcv1->error = error; |
| IBWARN("_do_madrpc failed; dport (%s)", |
| portid2str(dport)); |
| return NULL; |
| } |
| |
| mad = umad_get_mad(rcvbuf); |
| status = mad_get_field(mad, 0, IB_DRSMP_STATUS_F); |
| |
| /* check for exact match instead of only the redirect bit; |
| * that way, weird statuses cause an error, too */ |
| if (status == IB_MAD_STS_REDIRECT) { |
| /* update dport for next request and retry */ |
| /* bail if redirection fails */ |
| if (redirect_port(dport, mad)) |
| break; |
| } else |
| break; |
| } while (1); |
| |
| if ((rpc->mgtclass & IB_MAD_RPC_VERSION_MASK) == IB_MAD_RPC_VERSION1) |
| rpcv1->error = error; |
| rpc->rstatus = status; |
| |
| if (status != 0) { |
| ERRS("MAD completed with error status 0x%x; dport (%s)", |
| status, portid2str(dport)); |
| errno = EIO; |
| return NULL; |
| } |
| |
| if (rcvdata) |
| memcpy(rcvdata, mad + rpc->dataoffs, rpc->datasz); |
| |
| return rcvdata; |
| } |
| |
| void *mad_rpc_rmpp(const struct ibmad_port *port, ib_rpc_t * rpc, |
| ib_portid_t * dport, ib_rmpp_hdr_t * rmpp, void *data) |
| { |
| int status, len; |
| uint8_t sndbuf[1024], rcvbuf[1024], *mad; |
| ib_rpc_v1_t *rpcv1 = (ib_rpc_v1_t *)rpc; |
| int error = 0; |
| |
| memset(sndbuf, 0, umad_size() + IB_MAD_SIZE); |
| |
| DEBUG("rmpp %p data %p", rmpp, data); |
| |
| if ((rpc->mgtclass & IB_MAD_RPC_VERSION_MASK) == IB_MAD_RPC_VERSION1) |
| rpcv1->error = 0; |
| if ((len = mad_build_pkt(sndbuf, rpc, dport, rmpp, data)) < 0) |
| return NULL; |
| |
| if ((len = _do_madrpc(port->port_id, sndbuf, rcvbuf, |
| port->class_agents[rpc->mgtclass & 0xff], |
| len, mad_get_timeout(port, rpc->timeout), |
| mad_get_retries(port), &error)) < 0) { |
| if ((rpc->mgtclass & IB_MAD_RPC_VERSION_MASK) == IB_MAD_RPC_VERSION1) |
| rpcv1->error = error; |
| IBWARN("_do_madrpc failed; dport (%s)", portid2str(dport)); |
| return NULL; |
| } |
| |
| if ((rpc->mgtclass & IB_MAD_RPC_VERSION_MASK) == IB_MAD_RPC_VERSION1) |
| rpcv1->error = error; |
| |
| mad = umad_get_mad(rcvbuf); |
| |
| if ((status = mad_get_field(mad, 0, IB_MAD_STATUS_F)) != 0) { |
| ERRS("MAD completed with error status 0x%x; dport (%s)", |
| status, portid2str(dport)); |
| errno = EIO; |
| return NULL; |
| } |
| |
| if (rmpp) { |
| rmpp->flags = mad_get_field(mad, 0, IB_SA_RMPP_FLAGS_F); |
| if ((rmpp->flags & 0x3) && |
| mad_get_field(mad, 0, IB_SA_RMPP_VERS_F) != 1) { |
| IBWARN("bad rmpp version"); |
| return NULL; |
| } |
| rmpp->type = mad_get_field(mad, 0, IB_SA_RMPP_TYPE_F); |
| rmpp->status = mad_get_field(mad, 0, IB_SA_RMPP_STATUS_F); |
| DEBUG("rmpp type %d status %d", rmpp->type, rmpp->status); |
| rmpp->d1.u = mad_get_field(mad, 0, IB_SA_RMPP_D1_F); |
| rmpp->d2.u = mad_get_field(mad, 0, IB_SA_RMPP_D2_F); |
| } |
| |
| if (data) |
| memcpy(data, mad + rpc->dataoffs, rpc->datasz); |
| |
| rpc->recsz = mad_get_field(mad, 0, IB_SA_ATTROFFS_F); |
| |
| return data; |
| } |
| |
| void *madrpc(ib_rpc_t * rpc, ib_portid_t * dport, void *payload, void *rcvdata) |
| { |
| return mad_rpc(ibmp, rpc, dport, payload, rcvdata); |
| } |
| |
| void *madrpc_rmpp(ib_rpc_t * rpc, ib_portid_t * dport, ib_rmpp_hdr_t * rmpp, |
| void *data) |
| { |
| return mad_rpc_rmpp(ibmp, rpc, dport, rmpp, data); |
| } |
| |
| void |
| madrpc_init(char *dev_name, int dev_port, int *mgmt_classes, int num_classes) |
| { |
| int fd; |
| |
| if (umad_init() < 0) |
| IBPANIC("can't init UMAD library"); |
| |
| if ((fd = umad_open_port(dev_name, dev_port)) < 0) |
| IBPANIC("can't open UMAD port (%s:%d)", |
| dev_name ? dev_name : "(nil)", dev_port); |
| |
| if (num_classes >= MAX_CLASS) |
| IBPANIC("too many classes %d requested", num_classes); |
| |
| ibmp->port_id = fd; |
| memset(ibmp->class_agents, 0xff, sizeof ibmp->class_agents); |
| while (num_classes--) { |
| uint8_t rmpp_version = 0; |
| int mgmt = *mgmt_classes++; |
| |
| if (mgmt == IB_SA_CLASS) |
| rmpp_version = 1; |
| if (mad_register_client_via(mgmt, rmpp_version, ibmp) < 0) |
| IBPANIC("client_register for mgmt class %d failed", |
| mgmt); |
| } |
| } |
| |
| struct ibmad_port *mad_rpc_open_port(char *dev_name, int dev_port, |
| int *mgmt_classes, int num_classes) |
| { |
| struct ibmad_port *p; |
| int port_id; |
| char *debug_level_env; |
| |
| if (num_classes >= MAX_CLASS) { |
| IBWARN("too many classes %d requested", num_classes); |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| if (umad_init() < 0) { |
| IBWARN("can't init UMAD library"); |
| errno = ENODEV; |
| return NULL; |
| } |
| |
| debug_level_env = getenv("LIBIBMAD_DEBUG_LEVEL"); |
| if (debug_level_env) { |
| ibdebug = atoi(debug_level_env); |
| } |
| |
| p = malloc(sizeof(*p)); |
| if (!p) { |
| errno = ENOMEM; |
| return NULL; |
| } |
| memset(p, 0, sizeof(*p)); |
| |
| if ((port_id = umad_open_port(dev_name, dev_port)) < 0) { |
| IBWARN("can't open UMAD port (%s:%d)", dev_name, dev_port); |
| if (!errno) |
| errno = EIO; |
| free(p); |
| return NULL; |
| } |
| |
| p->port_id = port_id; |
| memset(p->class_agents, 0xff, sizeof p->class_agents); |
| while (num_classes--) { |
| uint8_t rmpp_version = 0; |
| int mgmt = *mgmt_classes++; |
| |
| if (mgmt == IB_SA_CLASS) |
| rmpp_version = 1; |
| if (mgmt < 0 || mgmt >= MAX_CLASS || |
| mad_register_client_via(mgmt, rmpp_version, p) < 0) { |
| IBWARN("client_register for mgmt %d failed", mgmt); |
| if (!errno) |
| errno = EINVAL; |
| umad_close_port(port_id); |
| free(p); |
| return NULL; |
| } |
| } |
| |
| return p; |
| } |
| |
| void mad_rpc_close_port(struct ibmad_port *port) |
| { |
| umad_close_port(port->port_id); |
| free(port); |
| } |
| |
| static int get_smi_gsi_pair(const char *ca_name, int portnum, struct ibmad_ports_pair *ports_pair, |
| unsigned enforce_smi) |
| { |
| struct umad_ca_pair ca_pair; |
| int smi_port_id = -1; |
| int gsi_port_id = -1; |
| int rc = -1; |
| rc = umad_get_smi_gsi_pair_by_ca_name(ca_name, portnum, &ca_pair, enforce_smi); |
| |
| if (rc < 0) { |
| IBWARN("Can't open UMAD port (%s) (%s:%d)", strerror(-rc), ca_name, portnum); |
| return rc; |
| } |
| smi_port_id = umad_open_port(ca_pair.smi_name, ca_pair.smi_preferred_port); |
| |
| if (smi_port_id < 0 && enforce_smi) { |
| IBWARN("Can't open SMI UMAD port (%s) (%s:%d)", strerror(-smi_port_id), ca_pair.smi_name, ca_pair.smi_preferred_port); |
| return smi_port_id; |
| } |
| |
| gsi_port_id = umad_open_port(ca_pair.gsi_name, ca_pair.gsi_preferred_port); |
| |
| if (gsi_port_id < 0) { |
| IBWARN("Can't open GSI UMAD port (%s) (%s:%d)", strerror(-gsi_port_id), ca_pair.gsi_name, ca_pair.gsi_preferred_port); |
| umad_close_port(smi_port_id); |
| return gsi_port_id; |
| } |
| |
| ports_pair->smi.port->port_id = smi_port_id; |
| ports_pair->gsi.port->port_id = gsi_port_id; |
| strncpy(ports_pair->smi.ca_name, ca_pair.smi_name, UMAD_CA_NAME_LEN); |
| strncpy(ports_pair->gsi.ca_name, ca_pair.gsi_name, UMAD_CA_NAME_LEN); |
| |
| return 0; |
| } |
| |
| static struct ibmad_port *get_port_for_class(struct ibmad_ports_pair *ports_pair, int mgmt_class, unsigned enforce_smi) |
| { |
| if (mgmt_class != IB_SMI_CLASS && mgmt_class != IB_SMI_DIRECT_CLASS) { |
| if (ports_pair->gsi.port->port_id < 0) { |
| IBWARN("required port for GSI is invalid"); |
| return NULL; |
| } |
| return ports_pair->gsi.port; |
| } |
| |
| if (ports_pair->smi.port->port_id < 0) { |
| if (enforce_smi) { |
| IBWARN("required port for SMI is invalid"); |
| return NULL; |
| } |
| return ports_pair->gsi.port; |
| } |
| |
| return ports_pair->smi.port; |
| } |
| |
| struct ibmad_ports_pair *mad_rpc_open_port2(char *dev_name, int dev_port, |
| int *mgmt_classes, int num_classes, unsigned enforce_smi) |
| { |
| struct ibmad_port *smi; |
| struct ibmad_port *gsi; |
| struct ibmad_ports_pair *ports_pair; |
| char *debug_level_env; |
| |
| if (num_classes >= MAX_CLASS) { |
| IBWARN("too many classes %d requested", num_classes); |
| errno = EINVAL; |
| return NULL; |
| } |
| |
| if (umad_init() < 0) { |
| IBWARN("can't init UMAD library"); |
| errno = ENODEV; |
| return NULL; |
| } |
| |
| debug_level_env = getenv("LIBIBMAD_DEBUG_LEVEL"); |
| if (debug_level_env) { |
| ibdebug = atoi(debug_level_env); |
| } |
| |
| smi = malloc(sizeof(*smi)); |
| if (!smi) { |
| errno = ENOMEM; |
| goto smi_error; |
| } |
| memset(smi, 0, sizeof(*smi)); |
| |
| gsi = malloc(sizeof(*gsi)); |
| if (!gsi) { |
| errno = ENOMEM; |
| goto gsi_error; |
| } |
| memset(gsi, 0, sizeof(*gsi)); |
| |
| ports_pair = malloc(sizeof(*ports_pair)); |
| if (!ports_pair) { |
| errno = ENOMEM; |
| goto ports_pair_error; |
| } |
| memset(ports_pair, 0, sizeof(*ports_pair)); |
| ports_pair->smi.port = smi; |
| ports_pair->gsi.port = gsi; |
| |
| if ((get_smi_gsi_pair(dev_name, dev_port, ports_pair, enforce_smi)) < 0) { |
| IBWARN("can't open UMAD port (%s:%d)", dev_name, dev_port); |
| if (!errno) |
| errno = EIO; |
| goto get_smi_error; |
| } |
| |
| memset(ports_pair->smi.port->class_agents, 0xff, sizeof ports_pair->smi.port->class_agents); |
| memset(ports_pair->gsi.port->class_agents, 0xff, sizeof ports_pair->gsi.port->class_agents); |
| while (num_classes--) { |
| uint8_t rmpp_version = 0; |
| int mgmt = *mgmt_classes++; |
| struct ibmad_port *p = get_port_for_class(ports_pair, mgmt, enforce_smi); |
| if (mgmt == IB_SA_CLASS) |
| rmpp_version = 1; |
| if (mgmt < 0 || mgmt >= MAX_CLASS || !p || |
| mad_register_client_via(mgmt, rmpp_version, p) < 0) { |
| IBWARN("client_register for mgmt %d failed", mgmt); |
| if (!errno) |
| errno = EINVAL; |
| goto mad_reg_error; |
| } |
| } |
| |
| return ports_pair; |
| |
| mad_reg_error: |
| umad_close_port(ports_pair->smi.port->port_id); |
| umad_close_port(ports_pair->gsi.port->port_id); |
| get_smi_error: |
| free(ports_pair); |
| ports_pair_error: |
| free(gsi); |
| gsi_error: |
| free(smi); |
| smi_error: |
| return NULL; |
| } |
| |
| void mad_rpc_close_port2(struct ibmad_ports_pair *srcport) |
| { |
| umad_close_port(srcport->smi.port->port_id); |
| umad_close_port(srcport->gsi.port->port_id); |
| free(srcport->smi.port); |
| free(srcport->gsi.port); |
| free(srcport); |
| } |