/* BEGIN_ICS_COPYRIGHT5 ****************************************
Copyright (c) 2015-2020, Intel Corporation
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.
* Neither the name of Intel Corporation nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ** END_ICS_COPYRIGHT5 ****************************************/
#include <cs_g.h>
#include <mai_g.h>
#include <mal_g.h>
#include <ib_macros.h>
#include <if3.h>
#include "opamgt_priv.h"
#include <ib_mad.h>
#include <ib_sa.h>
#include <iba/ib_pkt.h>
#include <iba/ib_mad.h>
#include <iba/ib_sm_priv.h>
#include <iba/public/statustext.h>
#include <iba/ib_rmpp.h>
#include <iba/stl_sm_priv.h>
#include <iba/stl_sa_priv.h>
#include <iba/stl_pm.h>
#include <iba/stl_pa_priv.h>
//==============================================================================
#define IB_ISSM_DEVICEPATH_MAXLEN 32
//==============================================================================
//==============================================================================
#define IB_HANDLE2DEV(handle) (( (handle) >> 8) & 0xFF)
#define IB_HANDLE2PORT(handle) (( (handle) ) & 0xFF)
#define IB_MAKEHANDLE(dev, port) \
((((dev) & 0xff) << 8) | ((port) & 0xff))
#define IB_FROMHANDLE(handle, dev, port) \
dev = IB_HANDLE2DEV(handle); \
port = IB_HANDLE2PORT(handle);
//==============================================================================
static uint8_t ib_truescale_oui[] = {OUI_TRUESCALE_0, OUI_TRUESCALE_1, OUI_TRUESCALE_2};
typedef struct
{
FILE *fp;
Lock_t lock;
} ib_issm_t;
static ib_issm_t ib_issm;
static struct omgt_class_args sm_class_args[] = {
{ IB_BASE_VERSION, MAD_CV_SUBN_DR, 1,
.is_responding_client=1, .is_trap_client=1, .is_report_client=0, .kernel_rmpp=0, .oui=0, .use_methods=0 },
{ IB_BASE_VERSION, MAD_CV_SUBN_LR, 1,
.is_responding_client=1, .is_trap_client=1, .is_report_client=0, .kernel_rmpp=0, .oui=0, .use_methods=0},
{ STL_BASE_VERSION, MAD_CV_SUBN_DR, STL_SM_CLASS_VERSION,
.is_responding_client=1, .is_trap_client=1, .is_report_client=0, .kernel_rmpp=0, .oui=0, .use_methods=0 },
{ STL_BASE_VERSION, MAD_CV_SUBN_LR, STL_SM_CLASS_VERSION,
.is_responding_client=1, .is_trap_client=1, .is_report_client=0, .kernel_rmpp=0, .oui=0, .use_methods=0 },
{ STL_BASE_VERSION, MAD_CV_VENDOR_DBSYNC, STL_SA_CLASS_VERSION,
.oui=ib_truescale_oui, .use_methods=1,
.methods={ FE_MNGR_PROBE_CMD, FE_MNGR_CLOSE_CMD,
FM_CMD_SHUTDOWN, SA_CM_GET, SA_CM_SET, SA_CM_GETTABLE }},
{ STL_BASE_VERSION, MAD_CV_SUBN_ADM, STL_SA_CLASS_VERSION,
.oui=0, .use_methods=1,
.methods={ SA_CM_GET, SA_CM_SET, SA_CM_GETTABLE,
SA_CM_GETTRACETABLE, SA_CM_GETMULTI, SA_CM_DELETE }},
{ IB_BASE_VERSION, MAD_CV_SUBN_ADM, IB_SUBN_ADM_CLASS_VERSION,
.oui=0, .use_methods=1,
.methods={ SA_CM_GET, SA_CM_SET, SA_CM_GETTABLE,
SA_CM_GETTRACETABLE, SA_CM_GETMULTI, SA_CM_DELETE }},
{ 0 }
};
static struct omgt_class_args fe_class_args[] = {
{ IB_BASE_VERSION, MAD_CV_VENDOR_FE, 1,
.kernel_rmpp=0, .oui=ib_truescale_oui, .use_methods=1,
.methods={ FE_MNGR_PROBE_CMD,
FE_MNGR_CLOSE_CMD, FM_CMD_SHUTDOWN, RMPP_CMD_GET, RMPP_CMD_GETTABLE }},
{ IB_BASE_VERSION, MAD_CV_VFI_PM, 1,
.kernel_rmpp=0, .oui=ib_truescale_oui, .use_methods=1,
.methods={ FE_CMD_RESP, RMPP_CMD_GET, RMPP_CMD_GETTABLE }},
{ 0 }
};
static struct omgt_class_args fe_proc_class_args[] = {
{ IB_BASE_VERSION, MAD_CV_VENDOR_FE, 1,
.kernel_rmpp=0, .oui=ib_truescale_oui, .use_methods=1,
.methods={ FE_MNGR_PROBE_CMD,
FE_MNGR_CLOSE_CMD, FM_CMD_SHUTDOWN, RMPP_CMD_GET, RMPP_CMD_GETTABLE }},
{ STL_BASE_VERSION, MAD_CV_VFI_PM, STL_PM_CLASS_VERSION,
.kernel_rmpp=0, .oui=ib_truescale_oui, .use_methods=1,
.methods={ FE_CMD_RESP }},
{ IB_BASE_VERSION, MAD_CV_SUBN_DR, 1,
.is_responding_client=0, .is_trap_client=0, .is_report_client=0, .kernel_rmpp=0, .oui=0, .use_methods=0 },
{ IB_BASE_VERSION, MAD_CV_SUBN_LR, 1,
.is_responding_client=0, .is_trap_client=0, .is_report_client=0, .kernel_rmpp=0, .oui=0, .use_methods=0},
{ STL_BASE_VERSION, MAD_CV_SUBN_DR, STL_SM_CLASS_VERSION,
.is_responding_client=0, .is_trap_client=0, .is_report_client=0, .kernel_rmpp=0, .oui=0, .use_methods=0 },
{ STL_BASE_VERSION, MAD_CV_SUBN_LR, STL_SM_CLASS_VERSION,
.is_responding_client=0, .is_trap_client=0, .is_report_client=0, .kernel_rmpp=0, .oui=0, .use_methods=0 },
{ STL_BASE_VERSION, MAD_CV_SUBN_ADM, STL_SA_CLASS_VERSION,
.is_responding_client=0, .is_trap_client=0, .is_report_client=0, .kernel_rmpp=0, .oui=0, .use_methods=0 },
{ IB_BASE_VERSION, MAD_CV_SUBN_ADM, IB_SUBN_ADM_CLASS_VERSION,
.is_responding_client=0, .is_trap_client=0, .is_report_client=0, .kernel_rmpp=0, .oui=0, .use_methods=0 },
{ 0 }
};
static struct omgt_class_args pm_class_args[] = {
{ STL_BASE_VERSION, MAD_CV_VFI_PM, STL_PM_CLASS_VERSION,
.kernel_rmpp = 0, .oui=ib_truescale_oui, .use_methods=1,
.methods={ FE_MNGR_PROBE_CMD, FE_MNGR_CLOSE_CMD, FM_CMD_SHUTDOWN,
STL_PA_CMD_GET, STL_PA_CMD_SET, STL_PA_CMD_GETTABLE }},
{ STL_BASE_VERSION, MAD_CV_PERF, STL_PM_CLASS_VERSION,
.is_responding_client=0, .is_trap_client=0, .is_report_client=0, .kernel_rmpp=0, .oui=0, .use_methods=0},
{ 0 }
};
int ib_instrumentJmMads = 0;
/** TODO: currently there is only one open to the opamgt later (translated into only one open file
* handle to the ib_umad driver) and shared by all the threads spawned in Esm/ib/src/ibaccess/smi/sm/sm_main.c,
* which is the reason behind the complicated filtering mechanism in the upper MAI layer. To streamline
* the operation, the field in struct mai_dc->hndl can be replaced with struct omgt_port * so that each
* thread that calls mai_open() can have a separate port open in opamgt (i.e., a separate file handle
* to the ib_umad driver): mai_open() -> mai_get_dc() -> ib_attach_sma(), the last function (ib_attach_sma)
* should call omgt_open_port_by_num(). Similarly, ib_detach_sma should call omgt_close_port() to close
* the corresponding port. In addition, all the sending/receiving function (ib_recv_sma, etc) should
* be modified to carrry the struct omgt_port * parameter install of the IBhandle parameter). Of course,
* the ib_init_devport() and ib_init_guid() should not be used any more or modified not to call the
* omgt_open_port_xx() function.
* Note: the xx_mai_to_wire() or xx_wire_to_mai function
* involve buffer copying, which is bad for performance.
* */
struct omgt_port *g_port_handle = NULL;
//==============================================================================
// We're hacking into MAI internals here. In the case of local packets, we
// just call mai_mad_process to short-circuit the path.
extern int mai_mad_process(Mai_t *mad, int *filterMatch);
extern uint32_t smDebugPerf;
extern uint32_t sm_debug;
//==============================================================================
static Status_t ib_mai_to_wire(Mai_t *mad, uint8_t *buf);
static Status_t stl_wire_to_mai(uint8_t *buf, Mai_t *mad, int len);
static void stl_mad_to_mai(MAD* mad, Mai_t *mai);
static Status_t stl_mai_to_wire(Mai_t *mad, uint8_t *buf, uint32_t *bufLen);
//==============================================================================
// ib_init_common
//==============================================================================
static Status_t ib_init_common(void)
{
Status_t status;
ib_issm.fp = NULL;
status = vs_lock_init(&ib_issm.lock, VLOCK_FREE, VLOCK_THREAD);
if (status != VSTATUS_OK) {
IB_LOG_ERRORRC("can't initialize issm device lock; rc:", status);
return status;
}
return VSTATUS_OK;
}
//==============================================================================
// ib_init_devport
//==============================================================================
Status_t
ib_init_devport(uint32_t *devp, uint32_t *portp, uint64_t *Guidp, struct omgt_params *session_params)
{
OMGT_STATUS_T status;
IB_ENTER(__func__, devp, portp, Guidp, 0);
// Check if the port is opened or
if (g_port_handle) {
uint64_t port_guid = 0;
int32_t hfi_num = -1;
uint8_t port_num = 0;
(void)omgt_port_get_port_guid(g_port_handle, &port_guid);
(void)omgt_port_get_hfi_num(g_port_handle, &hfi_num);
(void)omgt_port_get_hfi_port_num(g_port_handle, &port_num);
IB_LOG_ERROR_FMT(__func__, "one port (%d/%d GUID 0x%.16"CS64"x) is already opened",
hfi_num, port_num, port_guid);
return VSTATUS_BUSY;
}
status = ib_init_common();
if (status != VSTATUS_OK) {
IB_EXIT(__func__, status);
return status;
}
if (Guidp != NULL && *Guidp != 0ULL) {
status = omgt_open_port_by_guid(&g_port_handle, *Guidp, session_params);
if (status != FSUCCESS) {
IB_LOG_ERROR_FMT(__func__,
"Failed to bind to GUID 0x%.16"CS64"x; status: %u",
*Guidp, status);
IB_EXIT(__func__, VSTATUS_BAD);
return VSTATUS_BAD;
}
// note that the opamgt interface uses 1-based devices
if (devp != NULL) {
(void)omgt_port_get_hfi_num(g_port_handle, (int32_t *)devp);
(*devp)--;
}
if (portp != NULL) {
uint8_t port_num = 0;
(void)omgt_port_get_hfi_port_num(g_port_handle, &port_num);
*portp = port_num;
}
} else if (devp != NULL && portp != NULL) {
// note that the opamgt interface uses 1-based devices
status = omgt_open_port_by_num(&g_port_handle, *devp + 1, *portp, session_params);
if (status != FSUCCESS) {
// PR 128290 - MWHEINZ - This is a bit of a hack. Some parts of
// the stack use 0-based counting of ports and devices but other
// parts use 1-based. We assume the caller of this function
// is using zero-based counting, so we add 1 when we call
// omgt_open_port_by_num(), but we also assume the user is
// expecting a 1-based device number in their error messages.
IB_LOG_ERROR_FMT(__func__,
"Failed to bind to device %d, port %d; status: %u",
*devp + 1, *portp, status);
IB_EXIT(__func__, VSTATUS_BAD);
return VSTATUS_BAD;
}
if (Guidp != NULL) {
(void)omgt_port_get_port_guid(g_port_handle, Guidp);
}
} else {
IB_LOG_ERROR_FMT(__func__,
"Neither Guid nor device and port were supplied");
IB_EXIT(__func__, VSTATUS_BAD);
return VSTATUS_BAD;
}
IB_EXIT(__func__, VSTATUS_OK);
return VSTATUS_OK;
}
//==============================================================================
// ib_refresh_devport
//==============================================================================
Status_t
ib_refresh_devport(void)
{
IB_ENTER(__func__, 0, 0, 0, 0);
if (omgt_mad_refresh_port_pkey(g_port_handle) < 0) {
IB_LOG_ERROR_FMT(__func__,
"Failed to refresh UMAD pkeys");
IB_EXIT(__func__, VSTATUS_BAD);
return VSTATUS_BAD;
}
IB_EXIT(__func__, VSTATUS_OK);
return VSTATUS_OK;
}
//==============================================================================
// ib_shutdown
//==============================================================================
Status_t ib_shutdown(void)
{
IB_ENTER(__func__, 0, 0, 0, 0);
ib_disable_is_sm();
omgt_close_port(g_port_handle);
g_port_handle = NULL;
IB_EXIT(__func__, 0);
return VSTATUS_OK;
}
//==============================================================================
// ib_register_sm
//==============================================================================
Status_t
ib_register_sm(int queue_size)
{
FSTATUS status;
status = omgt_bind_classes(g_port_handle, sm_class_args);
if (status != FSUCCESS) {
IB_LOG_ERROR("Failed to register management classes;",
status);
IB_EXIT(__func__, status);
return status;
}
IB_EXIT(__func__, status);
return status;
}
//==============================================================================
// ib_register_fe
//==============================================================================
Status_t
ib_register_fe(int queue_size, uint8_t thread)
{
FSTATUS status;
IB_ENTER(__func__, 0, 0, 0, 0);
status = omgt_bind_classes(g_port_handle, (thread) ? fe_class_args : fe_proc_class_args);
if (status != FSUCCESS) {
IB_LOG_ERROR("Failed to register FE management classes;",
status);
IB_EXIT(__func__, status);
return status;
}
IB_EXIT(__func__, status);
return status;
}
//==============================================================================
// ib_register_pm
//==============================================================================
Status_t
ib_register_pm(int queue_size)
{
FSTATUS status;
IB_ENTER(__func__, 0, 0, 0, 0);
status = omgt_bind_classes(g_port_handle, pm_class_args);
if (status != FSUCCESS) {
IB_LOG_ERROR("Failed to register PM management classes;",
status);
IB_EXIT(__func__, status);
return status;
}
IB_EXIT(__func__, status);
return status;
}
//==============================================================================
static void
dump_mad(uint8_t *buf, int len, char *prefix)
{
int i;
char s[1024], *s0 = s;
memset(s, 0, sizeof(s));
for (i = 0; i < len; ++i) {
sprintf(s0, "%02x", *(buf + i)); s0 += 2;
if (i % 4 == 3) { sprintf(s0, " "); s0 += 1; }
if (i % 16 == 15) { IB_LOG_INFINI_INFO_FMT( __func__, "%s%s", prefix, s); s0 = s; memset(s, 0, sizeof(s)); }
}
}
//==============================================================================
// ib_recv_sma
//==============================================================================
Status_t
ib_recv_sma(IBhandle_t handle, Mai_t *mai, uint64_t timeout)
{
FSTATUS status;
Status_t rc;
uint8_t *buf = NULL;
size_t len = 0;
int dev, port;
int issmi;
struct omgt_mad_addr addr;
IB_ENTER(__func__, handle, mai, timeout, 0);
IB_FROMHANDLE(handle, dev, port);
// wait for inbound mad, no timeout
memset (&addr, 0, sizeof(addr));
retry:
do {
status = omgt_recv_mad_alloc(g_port_handle, &buf, &len, timeout, &addr);
}
while (status == FNOT_DONE);
// FSUCCESS - got a packet
// FTIMEOUT - wait for response timed out, get header of our request
// FREJECT - unexpected error processing a previous send or its response
// get back at least header of our request
if (status != FSUCCESS && status != FTIMEOUT && status != FREJECT) {
// unexpected problem getting packets
IB_LOG_ERRORSTR("Received SMA status:", iba_fstatus_msg(status));
IB_EXIT(__func__, VSTATUS_BAD);
return VSTATUS_BAD;
}
if (len < sizeof(MAD_COMMON)) {
// if too small we can't deal with it and must discard it
if (buf)
free(buf);
IB_LOG_ERROR_FMT(__func__, "bad size: %lu status: %s", (unsigned long)len, iba_fstatus_msg(status));
goto retry;
}
#ifdef DEBUG
else if (len > sizeof(MAD_COMMON) + STL_MAD_PAYLOAD_SIZE) {
// unexpected, we'll ignore the extra bytes of payload
IB_LOG_INFO_FMT(__func__, "large size: %lu status: %s", (unsigned long)len, iba_fstatus_msg(status));
}
#endif
// transfer STL packet to MAI structure
rc = stl_wire_to_mai(buf, mai, len);
free(buf);
if (status == FTIMEOUT || status == FREJECT) {
// extern const char *iba_fstatus_msg(int);
// printf("omgt_recv TYPE_ERROR: status=%s class=0x%x attr=0x%x method=0x%x\n",
// iba_fstatus_msg(status),mai->base.mclass, mai->base.aid, mai->base.method);
// this is a packet we tried to send but failed to send or
// failed to get a response for
mai->type = MAI_TYPE_ERROR;
// added info message as part of PR 115443 to show that the request timed out
if (status == FTIMEOUT && sm_debug && smDebugPerf) {
IB_LOG_INFINI_INFO_FMT(__func__, "FTIMEOUT of packet status=%s class=0x%x attr=0x%x method=0x%x TID=0x%.16"CS64"X\n",
iba_fstatus_msg(status), mai->base.mclass, mai->base.aid, mai->base.method, mai->base.tid);
}
}
if (rc != VSTATUS_OK) {
IB_LOG_ERRORRC("Error converting MAD from wire format; rc:", rc);
IB_EXIT(__func__, rc);
return rc;
}
// SMP's are SMI, all else is GSI
issmi = ((mai->base.mclass == MAD_CV_SUBN_LR || mai->base.mclass == MAD_CV_SUBN_DR)
? 1 : 0);
// fill in core MAI structure
mai->dev = dev;
mai->port = port;
mai->qp = issmi ? 0 : 1;
mai->active |=(MAI_ACT_DEV | MAI_ACT_PORT | MAI_ACT_QP);
// fill in packet header data
mai->addrInfo.qkey = addr.qkey;
mai->addrInfo.srcqp = addr.qpn;
mai->addrInfo.destqp = mai->qp;
mai->addrInfo.pkey = addr.pkey;
mai->addrInfo.sl = addr.sl;
if(mai->base.mclass == MAD_CV_SUBN_DR && ((DRStlSmp_t*)(mai->data))->DrSLID == STL_LID_PERMISSIVE && ((DRStlSmp_t*)(mai->data))->DrSLID == STL_LID_PERMISSIVE){
mai->addrInfo.slid = STL_LID_PERMISSIVE;
mai->addrInfo.dlid = STL_LID_PERMISSIVE;
} else {
mai->addrInfo.slid = addr.lid;
mai->addrInfo.dlid = addr.lid;
}
/*
if (qp == 1)
IB_LOG_WARN_FMT( __func__, "SL is %d (qp=%d, pkey=0x%x, slid= %d)", mai->addrInfo.sl, mai->qp, addr.pkey, mai->addrInfo.slid);
*/
mai->active |=MAI_ACT_ADDRINFO;
if (ib_instrumentJmMads
&& mai->base.mclass == 0x03 && mai->base.aid == 0xffb2) {
IB_LOG_INFINI_INFO_FMT(__func__,
"Received: mclass 0x%02x method 0x%02x aid 0x%04x amod 0x%08x",
mai->base.mclass, mai->base.method, mai->base.aid, mai->base.amod);
dump_mad(mai->data, (IB_SA_FULL_HEADER_SIZE + IB_SAMAD_DATA_COUNT), " ");
}
IB_EXIT(__func__, VSTATUS_OK);
return VSTATUS_OK;
}
//==============================================================================
// stl_send_sma
//==============================================================================
Status_t
stl_send_sma(IBhandle_t handle, Mai_t * mai, uint64_t timeout)
{
FSTATUS status;
Status_t rc;
uint32_t bufLen = 0;
uint8_t buf[STL_MAX_MAD_DATA];
int filterMatch;
int adjusted_timeout;
struct omgt_mad_addr addr;
IB_ENTER(__func__, handle, mai, 0, 0);
ASSERT(mai != NULL);
if (mai->type == MAI_TYPE_INTERNAL) {
IB_LOG_VERBOSE_FMT(__func__, "local delivery for MAD to 0x%08x on QP %d",
mai->addrInfo.dlid, mai->qp);
// for local delivery
(void)mai_mad_process(mai, &filterMatch);
IB_EXIT(__func__, VSTATUS_OK);
return VSTATUS_OK;
}
if ( ib_instrumentJmMads
&& mai->base.mclass == 0x03 && mai->base.aid == 0xffb2) {
IB_LOG_INFINI_INFO_FMT( __func__,
"Sent: mclass 0x%02x method 0x%02x aid 0x%04x amod 0x%08x",
mai->base.mclass, mai->base.method, mai->base.aid, mai->base.amod);
dump_mad(mai->data, sizeof(mai->data), " ");
}
switch (mai->base.bversion) {
case STL_BASE_VERSION:
rc = stl_mai_to_wire(mai, buf, &bufLen);
break;
case IB_BASE_VERSION:
rc = ib_mai_to_wire(mai, buf);
bufLen = IB_MAX_MAD_DATA;
break;
default:
rc = VSTATUS_ILLPARM;
break;
}
if (rc != VSTATUS_OK) {
IB_LOG_ERRORRC("converting MAD to wire format; rc:", rc);
IB_EXIT(__func__, rc);
return rc;
}
// only send a timeout that is 90% of what the caller thinks it should be.
// this is to give OFED a moment to clear the MAD off the send list before
// the caller retries. without this, the caller's retry might be rejected
// as a duplicate if it comes in too quickly
// TBD - once all callers fixed, use timeout as given
adjusted_timeout = (int)(timeout * 9 / 10 * VTIMER_1_MILLISEC / VTIMER_1S);
if (adjusted_timeout <= 0 && timeout > 0)
adjusted_timeout=1;
memset(&addr, 0, sizeof(addr));
addr.lid = mai->addrInfo.dlid;
addr.qpn = mai->addrInfo.destqp;
addr.qkey = mai->addrInfo.qkey;
addr.pkey = mai->addrInfo.pkey;
addr.sl = mai->addrInfo.sl;
//IB_LOG_INFINI_INFO_FMT(__func__, "Sending MAD of size %d bytes", bufLen);
status = omgt_send_mad2(g_port_handle, (void*)buf, bufLen, &addr, adjusted_timeout, 0);
if (status != FSUCCESS) {
if (mai->addrInfo.srcqp != 1) {
IB_LOG_INFO("Error sending packet via OPENIB interface; status:", status);
} else {
IB_LOG_INFO_FMT(__func__,
"Error sending packet via OPENIB interface; status: %u (sl= %d, pkey= 0x%x)",
status, mai->addrInfo.sl, mai->addrInfo.pkey);
}
IB_EXIT(__func__, VSTATUS_BAD);
return VSTATUS_BAD;
}
IB_EXIT(__func__, VSTATUS_OK);
return VSTATUS_OK;
}
//==============================================================================
// ib_attach_sma
//==============================================================================
Status_t
ib_attach_sma(int16_t dev, int8_t port, uint32_t qpn,
IBhandle_t *handlep, uint8_t *nodeTypep)
{
FSTATUS status;
uint8_t type;
IB_ENTER(__func__, dev, port, qpn, 0);
if (handlep != NULL)
*handlep = IB_MAKEHANDLE(dev, port);
if (nodeTypep != NULL)
{
status = omgt_port_get_node_type(g_port_handle, &type);
if (status == FSUCCESS)
*nodeTypep = type;
else
{
IB_LOG_WARN("failed to get node type; status:", status);
IB_EXIT(__func__, VSTATUS_BAD);
return VSTATUS_BAD;
}
}
IB_EXIT(__func__, VSTATUS_OK);
return VSTATUS_OK;
}
//==============================================================================
// ib_detach_sma
//==============================================================================
Status_t
ib_detach_sma(IBhandle_t handle)
{
return VSTATUS_OK;
}
//==============================================================================
// ib_init_sma
//==============================================================================
Status_t
ib_init_sma(uint32_t maxMadBuffers)
{
return VSTATUS_OK;
}
//==============================================================================
// ib_term_sma
//==============================================================================
Status_t
ib_term_sma(void)
{
return VSTATUS_OK;
}
//==============================================================================
// ib_shutdown_all
//==============================================================================
void
ib_shutdown_all(void)
{
}
//==============================================================================
// ib_enable_is_sm
//==============================================================================
Status_t ib_enable_is_sm(void)
{
Status_t status;
FSTATUS rc;
char dev[IB_ISSM_DEVICEPATH_MAXLEN+1];
IB_ENTER(__func__, 0, 0, 0, 0);
status = vs_lock(&ib_issm.lock);
if (status != VSTATUS_OK) {
vs_unlock(&ib_issm.lock);
IB_LOG_WARNRC("failed to acquire issm lock; rc:", status);
IB_EXIT(__func__, status);
return status;
}
rc = omgt_get_issm_device(g_port_handle, dev, IB_ISSM_DEVICEPATH_MAXLEN);
if (rc != FSUCCESS) {
IB_LOG_WARN("failed to resolve ISSM device name; status:", status);
IB_EXIT(__func__, VSTATUS_BAD);
return VSTATUS_BAD;
}
// if already open, silently skip
if (ib_issm.fp == NULL) {
ib_issm.fp = fopen(dev, "rb");
if (ib_issm.fp == NULL) {
vs_unlock(&ib_issm.lock);
IB_LOG_WARN0("failed to open issm device");
IB_EXIT(__func__, VSTATUS_BAD);
return VSTATUS_BAD;
}
}
vs_unlock(&ib_issm.lock);
IB_EXIT(__func__, VSTATUS_OK);
return VSTATUS_OK;
}
//==============================================================================
// ib_disable_is_sm
//==============================================================================
Status_t ib_disable_is_sm(void)
{
Status_t status;
IB_ENTER(__func__, 0, 0, 0, 0);
status = vs_lock(&ib_issm.lock);
if (status != VSTATUS_OK) {
IB_LOG_WARNRC("failed to acquire issm lock; rc:", status);
IB_EXIT(__func__, status);
return status;
}
if (ib_issm.fp != NULL) {
fclose(ib_issm.fp);
ib_issm.fp = NULL;
}
vs_unlock(&ib_issm.lock);
IB_EXIT(__func__, VSTATUS_OK);
return VSTATUS_OK;
}
//==============================================================================
// stl_mad_to_mai
//==============================================================================
static void stl_mad_to_mai(MAD* mad, Mai_t *mai)
{
mai->base.bversion = mad->common.BaseVersion;
mai->base.mclass = mad->common.MgmtClass;
mai->base.cversion = mad->common.ClassVersion;
mai->base.method = mad->common.mr.AsReg8;
if (mad->common.MgmtClass == MCLASS_SM_DIRECTED_ROUTE) {
mai->base.status = mad->common.u.DR.s.Status;
mai->base.hopCount = mad->common.u.DR.HopCount;
mai->base.hopPointer = mad->common.u.DR.HopPointer;
} else
mai->base.status = mad->common.u.NS.Status.AsReg16;
mai->base.tid = mad->common.TransactionID;
mai->base.aid = mad->common.AttributeID;
mai->base.amod = mad->common.AttributeModifier;
}
//==============================================================================
// ib_mai_to_wire
//==============================================================================
static Status_t
ib_mai_to_wire(Mai_t *mad, uint8_t *buf)
{
int isDrouted;
uint32_t mask = 0;
IB_ENTER(__func__, mad, buf, 0, 0);
// Validate the obvious parameters
if (mad == NULL) {
IB_LOG_ERROR0("Invalid parameter: 'mad'");
IB_EXIT(__func__, VSTATUS_ILLPARM);
return VSTATUS_ILLPARM;
}
// Make sure we have a MAD header
mask = MAI_ACT_TYPE;
if ((mad->active & mask) != mask) {
IB_LOG_ERROR0("MAI_ACT_TYPE not active!");
IB_EXIT(__func__, VSTATUS_INVALID_MADT);
return VSTATUS_INVALID_MADT;
}
// Validate the type of MAD
switch (mad->type) {
case MAI_TYPE_EXTERNAL:
IB_LOG_INFO("Type is MAD", mad->type);
// MADs require these fields
mask = MAI_ACT_ADDRINFO;
if ((mad->active & mask) != mask) {
IB_LOG_ERROR("Invalid Mai_t:", mad->active);
IB_EXIT(__func__, VSTATUS_INVALID_MAD);
return VSTATUS_INVALID_MAD;
}
break;
default:
IB_LOG_ERROR("Invalid MAD type:", mad->type);
IB_EXIT(__func__, VSTATUS_INVALID_TYPE);
return VSTATUS_INVALID_TYPE;
}
// Figure out about Direct Routed or LID routed
isDrouted = 0; /* Assume we are LID routed */
if (mad->base.mclass == MAD_CV_SUBN_DR)
isDrouted = 1;
// Setup reasonable defaults
// Check for 'Reserved' values in management class
if ( mad->base.mclass == 0 || mad->base.mclass == 2
|| (mad->base.mclass >= 0x50 && mad->base.mclass <= 0x80)
|| mad->base.mclass >= 0x82)
{
IB_LOG_ERROR("Reserved management class:",
mad->base.mclass);
IB_EXIT(__func__, VSTATUS_INVALID_MCLASS);
return VSTATUS_INVALID_MCLASS;
}
mad->base.bversion = MAD_BVERSION;
mad->base.rsvd3 = 0;
// copy MAD base header
if (mad->active & MAI_ACT_BASE) {
// Display the base header
if (IB_LOG_IS_INTERESTED(VS_LOG_INFO)) {
IB_LOG_INFO("Base BaseVersion ", mad->base.bversion);
IB_LOG_INFO(" MgmtClass ", mad->base.mclass);
IB_LOG_INFO(" ClassVersion ", mad->base.cversion);
IB_LOG_INFO(" Method ", mad->base.method);
IB_LOG_INFO(" Status ", mad->base.status);
IB_LOG_INFO(" HopPointer ", mad->base.hopPointer);
IB_LOG_INFO(" HopCount ", mad->base.hopCount);
IB_LOG_INFOLX(" TID ", mad->base.tid & 0xffffffffu);
IB_LOG_INFO(" AttributeID ", mad->base.aid);
IB_LOG_INFO(" AttributeMod ", mad->base.amod);
IB_LOG_INFO_FMT(__func__,"FullTID: 0x%016"CS64"x", mad->base.tid);
}
if (isDrouted) {
// clear status and ensure return bit is set if necessary
mad->base.status = mad->base.method == MAD_CM_GET_RESP
? MAD_DR_RETURN : MAD_DR_INITIAL;
}
memcpy(buf, &mad->base, sizeof(mad->base));
(void)BSWAP_MAD_HEADER((MAD *)buf);
}
// Copy data to the end of the wire buffer
if (!(mad->active & MAI_ACT_DATA) && mad->datasize <= 0) {
IB_LOG_ERROR("datasize size indeterminate:", mad->datasize);
IB_EXIT(__func__, VSTATUS_INVALID_MADLEN);
return VSTATUS_INVALID_MADLEN;
} else {
// we are constrained to send the number of bytes of the supported
// payload at all times
if (mad->datasize && mad->datasize > IB_MAD_PAYLOAD_SIZE) {
IB_LOG_ERROR("datasize out of range:", mad->datasize);
IB_EXIT(__func__, VSTATUS_TOO_LARGE);
return VSTATUS_TOO_LARGE;
} else {
// MAI_ACT_DATA not active; send the payload balance
mad->datasize = IB_MAD_PAYLOAD_SIZE;
}
memcpy(buf + sizeof(MAD_COMMON), mad->data, mad->datasize);
if (isDrouted) {
// magic numbers: DR LIDs are 32 bytes into a DR SMP header and
// are 2 bytes long each
*(uint16_t*)(buf + 32) = hton16(*(uint16_t*)(buf + 32));
*(uint16_t*)(buf + 34) = hton16(*(uint16_t*)(buf + 34));
}
}
// VENDOR MAD HACK:
// We use SA MAD headers for vendor classes, but the new vendor range
// requires a specific header with an OUI field 37 bytes into the MAD.
// In the SA header, this would be in the middle of the SM Key. Since
// we're not using anything past the RMPP header for vendor traffic, we'll
// just write the OUI directly into the MAD.
if ( mad->base.mclass >= IBA_VENDOR_RANGE2_START
&& mad->base.mclass <= IBA_VENDOR_RANGE2_END)
memcpy(buf + 37, ib_truescale_oui, 3);
// END HORRIBLE HORRIBLE HACK
IB_EXIT(__func__, VSTATUS_OK);
return VSTATUS_OK;
}
//==============================================================================
// stl_mai_to_wire
//==============================================================================
static Status_t
stl_mai_to_wire(Mai_t *mad, uint8_t *buf, uint32_t *bufLen)
{
int isDrouted;
uint32_t mask = 0;
IB_ENTER(__func__, mad, buf, 0, 0);
// Validate the obvious parameters
if (mad == NULL) {
IB_LOG_ERROR0("Invalid parameter: 'mad'");
IB_EXIT(__func__, VSTATUS_ILLPARM);
return VSTATUS_ILLPARM;
}
// Make sure we have a MAD header
mask = MAI_ACT_TYPE;
if ((mad->active & mask) != mask) {
IB_LOG_ERROR0("MAI_ACT_TYPE not active!");
IB_EXIT(__func__, VSTATUS_INVALID_MADT);
return VSTATUS_INVALID_MADT;
}
// Validate the type of MAD
switch (mad->type) {
case MAI_TYPE_EXTERNAL:
IB_LOG_INFO("Type is MAD", mad->type);
// MADs require these fields
mask = MAI_ACT_ADDRINFO;
if ((mad->active & mask) != mask) {
IB_LOG_ERROR("Invalid Mai_t:", mad->active);
IB_EXIT(__func__, VSTATUS_INVALID_MAD);
return VSTATUS_INVALID_MAD;
}
break;
default:
IB_LOG_ERROR("Invalid MAD type:", mad->type);
IB_EXIT(__func__, VSTATUS_INVALID_TYPE);
return VSTATUS_INVALID_TYPE;
}
// Figure out about Direct Routed or LID routed
isDrouted = 0; /* Assume we are LID routed */
if (mad->base.mclass == MAD_CV_SUBN_DR)
isDrouted = 1;
// Setup reasonable defaults
// Check for 'Reserved' values in management class
if (mad->base.mclass == 0 || mad->base.mclass == 2
|| (mad->base.mclass >= 0x50 && mad->base.mclass <= 0x80)
|| mad->base.mclass >= 0x82) {
IB_LOG_ERROR("Reserved management class:",
mad->base.mclass);
IB_EXIT(__func__, VSTATUS_INVALID_MCLASS);
return VSTATUS_INVALID_MCLASS;
}
mad->base.bversion = STL_BASE_VERSION;
mad->base.rsvd3 = 0;
// copy MAD base header
if (mad->active & MAI_ACT_BASE) {
// Display the base header
if (IB_LOG_IS_INTERESTED(VS_LOG_INFO)) {
IB_LOG_INFO("Base BaseVersion ", mad->base.bversion);
IB_LOG_INFO(" MgmtClass ", mad->base.mclass);
IB_LOG_INFO(" ClassVersion ", mad->base.cversion);
IB_LOG_INFO(" Method ", mad->base.method);
IB_LOG_INFO(" Status ", mad->base.status);
IB_LOG_INFO(" HopPointer ", mad->base.hopPointer);
IB_LOG_INFO(" HopCount ", mad->base.hopCount);
IB_LOG_INFOLX(" TID ", mad->base.tid & 0xffffffffu);
IB_LOG_INFO(" AttributeID ", mad->base.aid);
IB_LOG_INFO(" AttributeMod ", mad->base.amod);
IB_LOG_INFO_FMT(__func__, "FullTID: 0x%016"CS64"x", mad->base.tid);
}
if (isDrouted) {
// clear status and ensure return bit is set if necessary
mad->base.status = mad->base.method == MAD_CM_GET_RESP
? MAD_DR_RETURN : MAD_DR_INITIAL;
}
memcpy(buf, &mad->base, sizeof(mad->base));
(void)BSWAP_MAD_HEADER((MAD *)buf);
}
// Copy data to the end of the wire buffer
if (!(mad->active & MAI_ACT_DATA) && mad->datasize <= 0) {
IB_LOG_ERROR("datasize size indeterminate:", mad->datasize);
IB_EXIT(__func__, VSTATUS_INVALID_MADLEN);
return VSTATUS_INVALID_MADLEN;
} else {
if (mad->datasize) {
// assume the datasize field is valid
if (mad->datasize > STL_MAD_PAYLOAD_SIZE) {
IB_LOG_ERROR("datasize out of range:", mad->datasize);
IB_EXIT(__func__, VSTATUS_TOO_LARGE);
return VSTATUS_TOO_LARGE;
}
} else {
// in order to handle requests from legacy code, check validity of
// the datasize field. if not valid then, set the datasize to max payload size;
// otherwise datasize field is valid indicating a payload with no data.
if (!(mad->active & MAI_ACT_DATASIZE))
mad->datasize = STL_MAD_PAYLOAD_SIZE;
}
*bufLen = MAD_BASEHDR_SIZE + mad->datasize;
memcpy(buf + MAD_BASEHDR_SIZE, mad->data, mad->datasize);
if (isDrouted) {
STL_SMP *smp = (STL_SMP *)buf;
#if CPU_LE
smp->M_Key = ntoh64(smp->M_Key);
smp->SmpExt.DirectedRoute.DrSLID = ntoh32(smp->SmpExt.DirectedRoute.DrSLID);
smp->SmpExt.DirectedRoute.DrDLID = ntoh32(smp->SmpExt.DirectedRoute.DrDLID);
#endif
}
}
// VENDOR MAD HACK:
// We use SA MAD headers for vendor classes, but the new vendor range
// requires a specific header with an OUI field 37 bytes into the MAD.
// In the SA header, this would be in the middle of the SM Key. Since
// we're not using anything past the RMPP header for vendor traffic, we'll
// just write the OUI directly into the MAD.
if (mad->base.mclass >= IBA_VENDOR_RANGE2_START
&& mad->base.mclass <= IBA_VENDOR_RANGE2_END) {
memcpy(buf + 37, ib_truescale_oui, 3);
}
// END HORRIBLE HORRIBLE HACK
IB_EXIT(__func__, VSTATUS_OK);
return VSTATUS_OK;
}
//==============================================================================
// stl_wire_to_mai
//==============================================================================
static Status_t
stl_wire_to_mai(uint8_t *buf, Mai_t *mad, int len)
{
IB_ENTER(__func__, 0, 0, 0, 0);
// Validate the obvious parameters
if (mad == NULL) {
IB_LOG_ERROR0("NULL mad pointer passed");
IB_EXIT(__func__, VSTATUS_ILLPARM);
return VSTATUS_ILLPARM;
}
if (buf == NULL) {
IB_LOG_ERROR0("NULL buf pointer passed");
IB_EXIT(__func__, VSTATUS_ILLPARM);
return VSTATUS_ILLPARM;
}
// clear out the active mask on the Mai_t struct
memset(mad, 0, sizeof(Mai_t));
mad->type = MAI_TYPE_EXTERNAL; // Assume type MAD until told otherwise
mad->active = MAI_ACT_TYPE;
(void)BSWAP_MAD_HEADER((MAD *)buf);
(void)stl_mad_to_mai((MAD *)buf, mad);
// convert the data only if we can identify the header
if (mad->base.bversion != MAD_BVERSION && mad->base.bversion != STL_BASE_VERSION) {
IB_LOG_ERROR("Unknown MAD version:", mad->base.bversion);
return VSTATUS_BAD;
}
// Check for 'Reserved' values in management class
if (mad->base.mclass == 0 || mad->base.mclass == 2
|| (mad->base.mclass >= 0x50 && mad->base.mclass <= 0x80)
|| mad->base.mclass >= 0x82) {
IB_LOG_ERROR("Reserved management class:",
mad->base.mclass);
return VSTATUS_BAD;
}
if (mad->base.mclass == MAD_CV_SUBN_DR
&& mad->base.hopPointer > IBA_MAX_PATHSIZE) {
IB_LOG_ERROR("Unexpected hopPointer value:",
mad->base.hopPointer);
return VSTATUS_BAD;
}
// We can safely say the MAD header is valid now.
mad->active |= MAI_ACT_BASE;
if (IB_LOG_IS_INTERESTED(VS_LOG_INFO)) {
IB_LOG_INFO("Base BaseVersion ", mad->base.bversion);
IB_LOG_INFO(" MgmtClass ", mad->base.mclass);
IB_LOG_INFO(" ClassVersion ", mad->base.cversion);
IB_LOG_INFO(" Method ", mad->base.method);
IB_LOG_INFO(" Status ", mad->base.status);
IB_LOG_INFO(" HopPointer ", mad->base.hopPointer);
IB_LOG_INFO(" HopCount ", mad->base.hopCount);
IB_LOG_INFOLX(" TID ", mad->base.tid);
IB_LOG_INFO(" AttributeID ", mad->base.aid);
IB_LOG_INFO(" AttributeMod ", mad->base.amod);
IB_LOG_INFO_FMT(__func__, "FullTID: 0x%016"CS64"x", mad->base.tid);
}
// Copy the data, if no data leave it zeroed from memset above
if (len > MAD_BASEHDR_SIZE) {
memcpy(mad->data, buf + MAD_BASEHDR_SIZE, len - MAD_BASEHDR_SIZE);
mad->datasize = len - MAD_BASEHDR_SIZE;
} else {
mad->datasize = 0;
}
(void)vs_time_get(&mad->intime);
mad->active |= MAI_ACT_DATA | MAI_ACT_TSTAMP | MAI_ACT_FMASK;
IB_EXIT(__func__, VSTATUS_OK);
return VSTATUS_OK;
}