/* BEGIN_ICS_COPYRIGHT2 ****************************************
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_COPYRIGHT2 ****************************************/
/************************************************************************
* *
* FILE NAME *
* vfi.c *
* *
* DESCRIPTION *
* Library calls for interface 3 *
* *
* *
* *
* *
************************************************************************/
#include "ib_types.h"
#include "ib_mad.h"
#include "ib_status.h"
#include "cs_g.h"
#include "mai_g.h"
#include "mal_g.h"
#include "ib_sa.h"
#include "ib_status.h"
#include <if3.h>
#include "ib_macros.h"
#include "if3_def.h"
#include "vfi_g.h"
#include "cs_log.h"
#if defined( __LINUX__)
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#endif
#ifndef NULL
#define NULL (0)
#endif
Status_t
vfi_MngrQueryService(ManagerInfo_t * mi, VFI_SERVICE_RECORD * service,
uint32_t mask, int *count, VfiSvcRecCmp_t cmp);
/*=========================================================================
* Local defines
*=========================================================================
*/
#define SVC_SA_GET_PATH_CMASK (IB_PATH_RECORD_COMP_NUMBPATH | IB_PATH_RECORD_COMP_SGID | IB_PATH_RECORD_COMP_DGID)
typedef struct {
int cnt;
VFI_SERVICE_RECORD *sr;
int status;
VfiSvcRecCmp_t cmp;
uint8_t data[sizeof(VFI_SERVICE_RECORD)];
uint8_t dlen;
} cb_ctx_t;
static void
BuildRecordGID(VFI_SERVICE_RECORD * srp, ManagerInfo_t * mi)
{
srp->RID.ServiceGID.Type.Global.SubnetPrefix = mi->gidPrefix;
srp->RID.ServiceGID.Type.Global.InterfaceID = mi->guid.guid[(int)(mi->vfi_guid)];
srp->Reserved = 0;
}
Status_t
vfi_GetPortGuid(ManagerInfo_t * fp, uint32_t gididx)
{
Status_t status;
SA_MAD mad;
STL_NODE_RECORD nr, *pnr;
uint8_t buffer[10 * STL_SA_DATA_LEN];
uint32_t madRc, bufferLength = (10 * STL_SA_DATA_LEN);
IB_ENTER(__func__, fp, gididx, 0, 0);
memset ((void*)(&buffer), 0, sizeof(buffer));
if (gididx > VFI_MAX_PORT_GUID) {
IB_LOG_ERROR_FMT(__func__, "Illegal guid param gididx %d", gididx);
IB_EXIT(__func__, VSTATUS_ILLPARM);
return VSTATUS_ILLPARM;
}
// initialize the commom mad header fields of the SA MAD
memset((void *)(&mad), 0, sizeof(mad));
MAD_SET_METHOD_TYPE(&mad, SUBN_ADM_GET);
MAD_SET_VERSION_INFO(&mad, STL_BASE_VERSION, MCLASS_SUBN_ADM, STL_SA_CLASS_VERSION);
MAD_SET_ATTRIB_ID(&mad, STL_SA_ATTR_NODE_RECORD);
// setup lid to search on at SA
pnr = (STL_NODE_RECORD *)mad.Data;
pnr->RID.LID = fp->slid;
pnr->Reserved = 0;
// initialize SA MAD payload
(void)BSWAP_STL_NODE_RECORD(pnr);
// initialize SA MAD header fields
SA_MAD_SET_HEADER(&mad, SM_KEY, STL_NODE_RECORD_COMP_LID);
// send request to SA and wait for result
if ((status = if3_mngr_send_mad(fp->fdr, &mad, sizeof(STL_NODE_RECORD),
buffer,
&bufferLength, &madRc, NULL, NULL)) != VSTATUS_OK) {
IB_LOG_VERBOSE_FMT(__func__,
"Was not able to communicate with SA (SM/SA may have moved), status=%d ", status);
goto done;
}
if (madRc != 0) {
status = VSTATUS_NOT_FOUND;
IB_LOG_INFINI_INFOX("SA does not have Guid for port, SM probably still sweeping, status=", madRc);
goto done;
}
// retrieve response data
(void)BSWAPCOPY_STL_NODE_RECORD((STL_NODE_RECORD *)buffer, &nr);
memcpy((void *)&fp->guid.guid[gididx], (void *)&nr.NodeInfo.PortGUID, sizeof(uint64_t));
fp->vfi_guid = (uint8_t) gididx;
IB_LOG_VERBOSE_FMT(__func__,
"Guid at idx %d is 0x%"CS64"x",
gididx, (fp->guid.guid[gididx]));
if (fp->guid.guid[gididx] == 0ll) {
status = VSTATUS_NOT_FOUND;
IB_LOG_ERROR_FMT(__func__, "Invalid guid exist at index gididx %d", gididx);
goto done;
}
fp->guidIsValid = 1;
done:
IB_EXIT(__func__, status);
return(status);
}
Status_t
vfi_MngrDelService(ManagerInfo_t * mi, VFI_SERVICE_RECORD * service, uint32_t mask)
{
Status_t status;
SA_MAD mad;
uint32_t madRc;
int count=0;
uint8_t buffer[STL_SA_DATA_LEN];
uint32_t bufferLength = sizeof(buffer);
IB_ENTER(__func__, mi, service, mask, 0);
// query the SA for the specified service record
status = vfi_MngrQueryService(mi, service, mask, &count, NULL);
if (status || count == 0) {
IB_LOG_ERROR_FMT(__func__, "service record not found, rc:%u count:%d", status, count);
status = VSTATUS_NOT_FOUND;
IB_EXIT(__func__,status);
return status;
}
memset((void *)(&mad), 0, sizeof(mad));
// initialize SA MAD header fields
SA_MAD_SET_HEADER(&mad, SM_KEY, mask);
// initialize the commom mad header fields of the SA MAD
MAD_SET_METHOD_TYPE(&mad, SUBN_ADM_DELETE);
MAD_SET_VERSION_INFO(&mad, IB_BASE_VERSION, MCLASS_SUBN_ADM, IB_SUBN_ADM_CLASS_VERSION);
MAD_SET_ATTRIB_ID(&mad, SA_ATTRIB_SERVICE_RECORD);
// initialize SA MAD payload
memcpy(mad.Data, service, sizeof(IB_SERVICE_RECORD));
(void)BSWAP_IB_SERVICE_RECORD((IB_SERVICE_RECORD *)mad.Data);
if ((status = if3_mngr_send_mad(mi->fdr, &mad, sizeof(IB_SERVICE_RECORD),
buffer,
&bufferLength, &madRc, NULL, NULL)) != VSTATUS_OK) {
IB_LOG_INFINI_INFO_FMT(__func__,
"Was not able to communicate with SA (SM/SA may have moved), rc:%d", status);
goto done;
}
if (madRc != 0) {
IB_LOG_INFINI_INFOX("SA did not find record, resp:", madRc);
status = VSTATUS_NOT_FOUND;
}
done:
IB_LOG_INFO(__func__, status);
return(status);
}
static uint32_t
one_sr_callback(CBTxRxData_t *data, void *context)
{
uint32_t recordSize = sizeof(VFI_SERVICE_RECORD);
int ready, remaining, offset;
cb_ctx_t *c = (cb_ctx_t *)context;
VFI_SERVICE_RECORD sr;
//
// We use this method to avoid allocating large static buffers for
// receiving service records from the SA. We only want one to satisfy
// the fact that a matching record exist at the SA.
switch (data->dir) {
case CB_RX_DIR:
// this is the case we're after
break;
case CB_TX_DIR:
c->status = VSTATUS_BAD;
IB_LOG_ERROR0("unexpected tx callback dir=CB_TX_DIR");
return VSTATUS_DROP;
case CB_TO_DIR:
c->status = VSTATUS_TIMEOUT;
IB_LOG_INFO("timeout", c);
return VSTATUS_OK;
default:
c->status = VSTATUS_BAD;
IB_LOG_ERROR("invalid callback dir:", (int)data->dir);
return VSTATUS_DROP;
}
if (!data->data) {
// found zero length payload response packet, or error condition occurred
// during reception of the response packet
c->status = VSTATUS_BAD;
return c->status;
}
// first segment? strip off the first SR and store the rest
if (c->cnt) {
memcpy(c->sr, data->data, sizeof(IB_SERVICE_RECORD));
(void)BSWAP_IB_SERVICE_RECORD(c->sr);
c->status = VSTATUS_OK;
c->cnt--;
c->dlen = data->dlen - recordSize;
memcpy(c->data, data->data + recordSize, c->dlen);
if (c->cmp == NULL) {
// no comparison function? then we always keep the
// first, so we're done
return VSTATUS_DROP;
} else {
return VSTATUS_OK;
}
}
// pull off remaining SRs and compare with the first
// keep the "lowest" according to the comparison function
ready = c->dlen;
remaining = data->dlen;
offset = 0;
while (remaining + ready >= recordSize) {
memcpy(c->data + ready, data->data + offset, recordSize - ready);
memcpy(&sr, c->data, sizeof(IB_SERVICE_RECORD));
(void)BSWAP_IB_SERVICE_RECORD(&sr);
if (c->cmp(c->sr, &sr) > 0)
memcpy(c->sr, &sr, sizeof(sr));
offset += recordSize - ready;
remaining -= recordSize - ready;
ready = 0;
}
if (remaining)
memcpy(c->data, data->data + offset, remaining);
return VSTATUS_OK;
}
Status_t
vfi_MngrQueryService(ManagerInfo_t * mi, VFI_SERVICE_RECORD * service,
uint32_t mask, int *count, VfiSvcRecCmp_t cmp)
{
Status_t status;
SA_MAD mad;
uint32_t bufferLength = sizeof(VFI_SERVICE_RECORD), madRc;
VFI_SERVICE_RECORD insr;
cb_ctx_t cb;
IB_ENTER(__func__, mi, service, mask, 0);
*count = 0;
memset((void *)(&mad), 0, sizeof(mad));
// initialize SA MAD header fields
SA_MAD_SET_HEADER(&mad, SM_KEY, mask);
// initialize the commom mad header fields of the SA MAD
MAD_SET_METHOD_TYPE(&mad, SUBN_ADM_GETTABLE);
MAD_SET_VERSION_INFO(&mad, IB_BASE_VERSION, MCLASS_SUBN_ADM, IB_SUBN_ADM_CLASS_VERSION);
MAD_SET_ATTRIB_ID(&mad, SA_ATTRIB_SERVICE_RECORD);
// initialize callback context
cb.cnt = 1;
cb.status = VSTATUS_BAD;
cb.sr = &insr;
cb.cmp = cmp;
cb.dlen = 0;
// initialize SA MAD payload
memcpy(mad.Data, service, sizeof(IB_SERVICE_RECORD));
(void)BSWAP_IB_SERVICE_RECORD((IB_SERVICE_RECORD *)mad.Data);
// send request to SA and wait for result
if ((status = if3_mngr_send_mad(mi->fdr,
&mad, sizeof(IB_SERVICE_RECORD),
NULL,
&bufferLength, &madRc, one_sr_callback,
&cb)) != VSTATUS_OK) {
IB_LOG_INFINI_INFOX("query to SA for service record failed (SM/SA may have moved), rc:", status);
goto done;
}
if (madRc != 0) {
IB_LOG_INFO("No records found at SA", madRc);
status = VSTATUS_OK;
goto done;
}
if (cb.status != VSTATUS_OK) {
IB_LOG_ERROR_FMT(__func__, "Callback protocol error %d", cb.status);
status = cb.status;
goto done;
}
*count = 1;
IB_LOG_INFO0(Log_StrDup((const char*)insr.ServiceName));
IB_LOG_INFO("insr.serviceId is ", insr.RID.ServiceID);
memcpy((void *)(service),(void *)(&insr),sizeof(IB_SERVICE_RECORD));
done:
IB_EXIT(__func__, status);
return(status);
}
Status_t
vfi_MngrQuerySrvPath(ManagerInfo_t * mi,
VFI_SERVICE_RECORD * srp, int *count,
IB_PATH_RECORD * pathRecordp)
{
Status_t status;
SA_MAD mad;
uint32_t madRc, plen;
uint8_t buffer[STL_IBA_SUBN_ADM_DATASIZE];
uint32_t bufferLength = sizeof(buffer);
IB_ENTER(__func__, mi, srp, count, pathRecordp);
memset((void *)(pathRecordp), 0, sizeof(*pathRecordp));
// construct source GID.
pathRecordp->SGID.Type.Global.SubnetPrefix = mi->gidPrefix;
pathRecordp->SGID.Type.Global.InterfaceID = mi->guid.guid[0];
pathRecordp->DGID = srp->RID.ServiceGID;
memset((void *)(&mad), 0, sizeof(mad));
// initialize SA MAD header fields
SA_MAD_SET_HEADER(&mad, SM_KEY, SVC_SA_GET_PATH_CMASK);
// set the specified max number of path records to be returned from
// the SA
pathRecordp->NumbPath = *count;
// initialize the commom mad header fields of the SA MAD
MAD_SET_METHOD_TYPE(&mad, SUBN_ADM_GETTABLE);
MAD_SET_VERSION_INFO(&mad, IB_BASE_VERSION, MCLASS_SUBN_ADM, IB_SUBN_ADM_CLASS_VERSION);
MAD_SET_ATTRIB_ID(&mad, SA_ATTRIB_PATH_RECORD);
// initialize SA MAD payload
(void)BSWAPCOPY_IB_PATH_RECORD(pathRecordp, (IB_PATH_RECORD *)mad.Data);
*count = 0;
status = if3_mngr_send_mad(mi->fdr,
&mad, sizeof(IB_PATH_RECORD),
buffer, &bufferLength,
&madRc, NULL, NULL);
if (status != VSTATUS_OK) {
IB_LOG_VERBOSERC("query to SA for server path failed (SM/SA may have moved), rc:", status);
IB_EXIT(__func__, status);
return (status);
}
if (madRc != 0) {
IB_LOG_VERBOSEX("server path query to SA returned with status=", madRc);
status = VSTATUS_BAD;
} else {
// determine how many records were returned
if (bufferLength % (sizeof(IB_PATH_RECORD))) {
IB_LOG_ERROR
("length does not align with record size ",
bufferLength);
status = VSTATUS_BAD;
IB_EXIT(__func__, status);
return (status);
}
plen = bufferLength / (sizeof(IB_PATH_RECORD));
*count = plen;
{
IB_LID lid;
uint16_t sl;
for (plen = 0; plen < (uint32_t)(*count); plen++) {
(void)BSWAPCOPY_IB_PATH_RECORD((IB_PATH_RECORD *)buffer + plen * (sizeof(IB_PATH_RECORD)),
pathRecordp + plen);
lid = ((pathRecordp + plen)->DLID);
sl = ((pathRecordp + plen)->u2.s.SL);
if (IB_LOG_IS_INTERESTED(VS_LOG_VERBOSE)) {
IB_LOG_VERBOSE(" PathREC : ", plen);
IB_LOG_VERBOSEX(" DLid : ", lid);
IB_LOG_VERBOSEX(" SLid : ",
((pathRecordp +
plen)->SLID));
IB_LOG_VERBOSE(" SL : ", sl);
}
}
}
}
IB_EXIT(__func__, status);
return (status);
}
Status_t
vfi_mngr_register(IBhandle_t fd, uint8_t mclass, int gididx,
VFI_SERVICE_RECORD * service, uint64_t mask, int option)
{
Status_t status,
rc;
VFI_SERVICE_RECORD servRec;
VFI_SERVICE_RECORD *serviceRecordp = service;
ManagerInfo_t *mi;
SA_MAD mad;
uint8_t buffer[STL_SA_DATA_LEN];
uint32_t madRc, bufferLength = sizeof(buffer);
int found,cnt=0;
uint64_t guid=0;
IB_ENTER(__func__, gididx, mask, 0, 0);
if (service == NULL ||
gididx < 0 || gididx > VFI_MAX_PORT_GUID || mask == 0 ||
(option != VFI_REGFORCE_FABRIC && option != VFI_REGTRY_FABRIC)) {
rc = VSTATUS_ILLPARM;
IB_LOG_ERROR("illegal param", rc);
IB_EXIT(__func__, rc);
return rc;
}
// find the MAI handle related information
status = if3_mngr_locate_minfo(fd, &mi);
if (status) {
IB_LOG_ERROR("minfo not found", status);
IB_EXIT(__func__, status);
return status;
}
// verify access to the SA by querying the the Port GUID of the SA
status = vfi_GetPortGuid(mi, gididx);
if (status != VSTATUS_OK) {
IB_EXIT(__func__, status);
return(status);
}
delete_next:
// build service record
BuildRecordGID(serviceRecordp, mi);
// preserve service record that will be sent when registering
if (cnt == 0) memcpy(&servRec,serviceRecordp,sizeof(servRec));
// query for the existence of the service record with the SA
status = vfi_MngrQueryService(mi, serviceRecordp, mask, &found, NULL);
if (status != VSTATUS_OK) {
IB_EXIT(__func__, status);
return(status);
}
guid = serviceRecordp->RID.ServiceGID.Type.Global.InterfaceID;
if (found != 0) {
if (option == VFI_REGTRY_FABRIC) {
IB_LOG_VERBOSELX("Registration exist already, GUID ", guid);
status = VSTATUS_BUSY;
IB_EXIT(__func__, status);
return(status);
}
// force the registration by deleting the existing service record
status = vfi_MngrDelService(mi, serviceRecordp, mask);
if (status != VSTATUS_OK) {
if (status == VSTATUS_NOT_FOUND) {
IB_LOG_VERBOSELX("service was not found, GUID ", guid);
} else {
status = VSTATUS_OK;
IB_LOG_VERBOSELX("Could not delete service record, GUID ", guid);
IB_EXIT(__func__, status);
return(status);
}
} else {
cnt++;
IB_LOG_INFO0("Successfully deleted service");
if (found > 1) {
// restore service record to initial state
memcpy(serviceRecordp,&servRec,sizeof(servRec));
goto delete_next;
}
}
}
//
// now proceed with the registration
memset((void *)(&mad), 0, sizeof(mad));
// initialize SA MAD header fields
SA_MAD_SET_HEADER(&mad, SM_KEY, mask);
// initialize the commom mad header fields of the SA MAD
MAD_SET_METHOD_TYPE(&mad, SUBN_ADM_SET);
MAD_SET_VERSION_INFO(&mad, IB_BASE_VERSION, MCLASS_SUBN_ADM, IB_SUBN_ADM_CLASS_VERSION);
MAD_SET_ATTRIB_ID(&mad, SA_ATTRIB_SERVICE_RECORD);
// initialize SA MAD payload
memcpy(mad.Data, &servRec, sizeof(IB_SERVICE_RECORD));
(void)BSWAP_IB_SERVICE_RECORD((IB_SERVICE_RECORD *)mad.Data);
// send service record to SA and wait for result
if ((status = if3_mngr_send_mad(mi->fdr, &mad, sizeof(servRec),
buffer,
&bufferLength, &madRc, NULL, NULL)) != VSTATUS_OK) {
IB_LOG_VERBOSERC("could not talk to SA (SM/SA may have moved) rc:", status);
goto done;
}
if (madRc != 0) {
IB_LOG_VERBOSELX("service record registration with SA returned status=", madRc);
status = VSTATUS_BAD;
}
done:
memcpy(service,&servRec,sizeof(IB_SERVICE_RECORD));
IB_EXIT(__func__, status);
return(status);
}
Status_t
vfi_mngr_unregister(IBhandle_t fd, uint8_t mclass, int gididx,
VFI_SERVICE_RECORD * service, uint64_t mask)
{
Status_t status,
rc;
VFI_SERVICE_RECORD *serviceRecordp = service;
ManagerInfo_t *mi;
IB_ENTER(__func__, gididx, mask, 0, 0);
if (service == NULL ||
gididx < 0 || gididx > VFI_MAX_PORT_GUID || mask == 0 ) {
rc = VSTATUS_ILLPARM;
IB_LOG_ERROR("illegal param", rc);
IB_EXIT(__func__, rc);
return rc;
}
// find the MAI handle related information
status = if3_mngr_locate_minfo(fd, &mi);
if (status) {
IB_LOG_ERROR("minfo not found", status);
IB_EXIT(__func__, status);
return status;
}
status = vfi_GetPortGuid(mi, gididx);
if (status != VSTATUS_OK) {
IB_EXIT(__func__, status);
return(status);
}
// build service record
BuildRecordGID(serviceRecordp, mi);
status = vfi_MngrDelService(mi, serviceRecordp, mask);
if (status != VSTATUS_OK) {
IB_LOG_INFINI_INFORC("Could not delete service rc:", status);
}
IB_EXIT(__func__, status);
return(status);
}
Status_t
vfi_mngr_find(IBhandle_t fd, uint8_t mclass, int slmc,
VFI_SERVICE_RECORD * service, uint64_t mask,
int *count, IB_PATH_RECORD * pbuff)
{
return vfi_mngr_find_cmp(fd, mclass, slmc, service,
mask, count, pbuff, NULL);
}
Status_t
vfi_mngr_find_cmp(IBhandle_t fd, uint8_t mclass, int slmc,
VFI_SERVICE_RECORD * service, uint64_t mask,
int *count, IB_PATH_RECORD * pbuff, VfiSvcRecCmp_t cmp)
{
Status_t status,
rc;
VFI_SERVICE_RECORD *serviceRecordp = service;
ManagerInfo_t *mi;
int found;
IB_ENTER(__func__, slmc, mask, 0, 0);
if (service == NULL || mask == 0 ||
pbuff == NULL || count == NULL ||
*count <= 0 || *count > VFI_MAX_SERVICE_PATH ||
slmc < 0 || slmc > VFI_MAX_LMC) {
rc = VSTATUS_ILLPARM;
IB_LOG_ERROR("illegal param", rc);
IB_EXIT(__func__, rc);
return rc;
}
// find the MAI handle related information
status = if3_mngr_locate_minfo(fd, &mi);
if (status) {
IB_LOG_ERROR("minfo not found", status);
IB_EXIT(__func__, status);
return status;
}
// query for the existence of a port guid info record with the SA
status = vfi_GetPortGuid(mi, VFI_DEFAULT_GUID);
if (status != VSTATUS_OK) {
IB_EXIT(__func__, status);
return(status);
}
// query for the existence of a service record with the SA
status = vfi_MngrQueryService(mi, serviceRecordp, mask, &found, cmp);
if (status != VSTATUS_OK) {
IB_EXIT(__func__, status);
return(status);
}
if (found == 0) {
status = VSTATUS_NOT_FOUND;
IB_LOG_INFO("Registration not found", status);
IB_EXIT(__func__, status);
return(status);
}
// query for the service path records
status = vfi_MngrQuerySrvPath(mi, serviceRecordp, count, pbuff);
IB_EXIT(__func__, status);
return(status);
}