/* BEGIN_ICS_COPYRIGHT4 ****************************************
Copyright (c) 2015-2017, 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_COPYRIGHT4 ****************************************/
#include "smamain.h"
//
// SmaPostSend
//
// All SMPs are sent out by the SM with this call. The caller can send one
// to many SMPs by listing each SMP through a linked list of SMP_BLOCKs.
// The user will specify the total SMPs contained in the list.
//
//
// INPUTS:
//
// SmObject -Object returned in the Open call.
//
// NumSmps -Number of SMPs to send
//
// SmpBlockList -Linked list of send requests contained in SMP_BLOCKs
//
// PostCallback -A callback for SMPs Posted through SmpBlockList
//
// PostContext -user context that will be passed back on a post callback
//
//
// OUTPUTS:
//
//
// RETURNS:
//
//
// IRQL:
//
//
FSTATUS
iba_smi_post_send(
IN SMA_OBJECT *SmObject,
IN uint32 NumSmps,
IN SMP_BLOCK *SmpBlockList,
IN SM_POST_CALLBACK *PostCallback,
IN void *PostContext
)
{
FSTATUS status = FSUCCESS;
boolean bLocal = TRUE;
uint32 smps, tmpSmps;
POOL_DATA_BLOCK *pReq, *pSmpBlock;
SMA_CA_OBJ_PRIVATE *pCaObj;
SMA_PORT_TABLE_PRIV *pPortTbl;
SMP_LIST *pSmpList;
STL_SMP *pSmp;
uint8 portNo;
_DBG_ENTER_LVL ( _DBG_LVL_SEND, iba_smi_post_send );
// Check params
smps = 0;
pReq = (POOL_DATA_BLOCK *)SmpBlockList;
while ( smps < NumSmps )
{
pSmp = (STL_SMP*)pReq->Block.Smp;
if (pReq->Block.SmpByteCount == 0)
{
if (pSmp->common.BaseVersion == IB_BASE_VERSION)
pReq->Block.SmpByteCount = IB_MAD_BLOCK_SIZE;
else
pReq->Block.SmpByteCount = STL_MAD_BLOCK_SIZE;
}
// Get CaContext
pCaObj = (SMA_CA_OBJ_PRIVATE *)pReq->Block.SmaCaContext;
if ( NULL != pCaObj )
{
pPortTbl = (SMA_PORT_TABLE_PRIV *)pCaObj->CaPublic.PortTbl;
// Check if Port number is valid
if (pCaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0)
{
if ( pReq->Block.PortNumber != 0 )
{
// Error return, free buffers...
_DBG_ERROR (( "Invalid Port!\n" ));
pReq->Block.Status = status = FINVALID_PARAMETER;
break;
}
portNo = 0;
}
else if ( pReq->Block.PortNumber < 1
|| pReq->Block.PortNumber > pCaObj->CaPublic.Ports )
{
// Error return, free buffers...
_DBG_ERROR (( "Invalid Port!\n" ));
pReq->Block.Status = status = FINVALID_PARAMETER;
break;
}
else
{
portNo = pReq->Block.PortNumber - 1;
}
if ( QP_DESTROY == \
pPortTbl[portNo].PortBlock->Qp0Control )
{
_DBG_ERROR (( "Qp set to Destroy state!\n" ));
pReq->Block.Status = status = FINVALID_STATE;
break;
}
else
{
// check SLID, 0 is invalid also used as special internal value
if ( 0 == pReq->Block.SLID)
{
_DBG_ERROR (( "SLID 0 !\n" ));
pReq->Block.Status = status = FINVALID_PARAMETER;
break;
}
// Do some sanity checks for outgoing SMP from SM
if ( 1 == pSmp->common.mr.s.R )
{
// Only SM_INFO can be passed up from SM as a Response
if ( MCLASS_ATTRIB_ID_SM_INFO != pSmp->common.AttributeID )
{
_DBG_ERROR (( "R bit set!\n" ));
pReq->Block.Status = status = FINVALID_PARAMETER;
break;
}
// Validate D bit, HopPointer and HopCount
// is per IBTA 1.1 sec 14.2.2.3
if ( MCLASS_SM_DIRECTED_ROUTE == pSmp->common.MgmtClass )
{
// D bit should be set
if ( 0 == pSmp->common.u.DR.s.D )
{
_DBG_ERROR ((
"Status D bit mismatch!\n" ));
pReq->Block.Status = status = FINVALID_PARAMETER;
break;
}
#if 0 // tests may not be valid if we use this interface to SMINFO ourselves
// HopCount cannot be zero at SM level in a response
if (0 == pSmp->common.u.DR.HopCount)
{
_DBG_ERROR ((
"HopCount zero response not available to SM!\n" ));
pReq->Block.Status = status = FINVALID_PARAMETER;
break;
}
// HopPointer must be equal to HopCount + 1
if (0 != pSmp->common.u.DR.HopCount)
{
if ( pSmp->common.u.DR.HopPointer !=
(pSmp->common.u.DR.HopCount+1))
{
_DBG_ERROR ((
"(HopPointer != HopCount+1) mismatch!\n" ));
pReq->Block.Status = status = FINVALID_PARAMETER;
break;
}
} // HC check
#endif
} // DR Smp check
} // R bit == 1 check
else
{
if ( MCLASS_SM_DIRECTED_ROUTE == pSmp->common.MgmtClass )
{
// validate per IBTA 1.1 sec 14.2.2.1
// D bit should be zero & HP should be zero
if ( 0 != ( pSmp->common.u.DR.s.D | \
pSmp->common.u.DR.HopPointer ))
{
_DBG_ERROR ((
"Status D bit or HopPointer non-zero mismatch!\n" ));
pReq->Block.Status = status = FINVALID_PARAMETER;
break;
}
// Local loopback check
if (( 0 == pSmp->common.u.DR.HopCount ) && \
( pReq->Block.DLID != STL_LID_PERMISSIVE ))
{
_DBG_ERROR ((
"Local loopback with DR DLID not Permissive!\n" ));
pReq->Block.Status = status = FINVALID_PARAMETER;
break;
}
} // DR Smp check
} // R bit == 0 check
} // Port check
}
else
{
_DBG_ERROR ((
"Bad CaContext!!!\n" ));
pReq->Block.Status = status = FINVALID_PARAMETER;
break;
}
// We need to trigger on a need basis to optimize interrupts.
// Look up port out usage
pReq->CQTrigger = TRUE;
// Do all post requests
smps++;
pReq = (POOL_DATA_BLOCK*)pReq->Block.Next;
} // Query all smps to be posted
// Only proceed if success
if ( FSUCCESS == status )
{
// Load Smp list
smps = 0;
pSmpList = (SMP_LIST*)MemoryAllocateAndClear(sizeof(SMP_LIST), FALSE, SMA_MEM_TAG);
if (pSmpList == NULL) {
status = FINSUFFICIENT_MEMORY;
goto exit;
}
// Prepare Posts and send out
pSmpList->SmpsIn = NumSmps; // input
AtomicWrite(&pSmpList->SmpsOut, 0); // output
pSmpList->SmpsInError = 0; // errors
pSmpList->SmpBlock = SmpBlockList;
pSmpList->PostCallback = PostCallback;
pSmpList->Context = PostContext;
// walk through list
pReq = (POOL_DATA_BLOCK *)SmpBlockList;
while ( smps < NumSmps )
{
SMI_ACTION SmiAction;
// link the list for active lookup in completions
pReq->Base = pSmpList;
pReq->Block.Status = 0;
pSmp = (STL_SMP*)pReq->Block.Smp;
// Get CaContext
pCaObj = (SMA_CA_OBJ_PRIVATE *)pReq->Block.SmaCaContext;
pPortTbl = (SMA_PORT_TABLE_PRIV *)pCaObj->CaPublic.PortTbl;
// Clear out usage fields
pReq->AvHandle = 0;
SmiAction = SmaProcessSmiSmp(pCaObj, pReq, FALSE);
if (pCaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0)
{
portNo = 0;
}
else
{
portNo = pReq->Block.PortNumber - 1;
}
// LID test below is not perfect since LMC should be included
// in computation. However if its wrong and we put on wire
// hardware should post back to us anyway, so not a big deal
if (SMI_TO_LOCAL == SmiAction
|| SMI_TO_LOCAL_SM == SmiAction
|| (SMI_LID_ROUTED == SmiAction
&& pReq->Block.DLID ==
(pPortTbl[portNo].PortBlock->Public.Address.BaseLID | pReq->Block.PathBits)
)
)
{
// Local loopback
tmpSmps = 1;
// grab a memory block
status = iba_smi_pool_get(
SmObject,
&tmpSmps,
(SMP_BLOCK **)&pSmpBlock
);
if ( FSUCCESS == status )
{
// Call internally and post on Recvd Q
MemoryCopy(
pSmpBlock->Block.Smp,
pReq->Block.Smp,
sizeof(STL_SMP)
);
// call common local function
pSmpBlock->Block.SmaCaContext = pReq->Block.SmaCaContext;
pSmpBlock->Block.PortNumber = pReq->Block.PortNumber;
pSmpBlock->Block.SmpByteCount = pReq->Block.SmpByteCount;
pSmpBlock->Block.SLID = pReq->Block.SLID;
pSmpBlock->Block.DLID = pReq->Block.DLID;
pSmpBlock->Block.PathBits = pReq->Block.PathBits;
pSmpBlock->Block.ServiceLevel = pReq->Block.ServiceLevel;
pSmpBlock->Block.StaticRate = pReq->Block.StaticRate;
if (SMI_TO_LOCAL == SmiAction || SMI_LID_ROUTED == SmiAction)
{
SmiAction = SmaProcessLocalSmp(pCaObj, pSmpBlock);
}
if (SMI_DISCARD == SmiAction)
{
(void)iba_smi_pool_put(
SmObject,
(SMP_BLOCK *)pSmpBlock);
} else {
// assume any SMI_TO_LOCAL, SMI_TO_LOCAL_SM or SMI_SEND
// or SMI_LID_ROUTED response
// needs to go back to the requestor
// so dump block in recv
SmaAddToRecvQ(pSmpBlock);
}
}
else
{
pReq->Block.Status = FINSUFFICIENT_MEMORY;
status = FSUCCESS;
}
// Add to SendQ (eg. PostSend send completions)
SmaAddToSendQ( pReq );
}
else if ((SMI_SEND == SmiAction)
|| (SMI_LID_ROUTED == SmiAction))
{
// Set local callback off
bLocal = FALSE;
status = SmaPostSmp(
pCaObj,
pReq );
if (FSUCCESS != status)
{
// discarded - unable to send
// Add to SendQ (eg. PostSend send completions)
SmaAddToSendQ( pReq );
status = FSUCCESS;
}
} else {
// discarded
// Add to SendQ (eg. PostSend send completions)
SmaAddToSendQ( pReq );
}
// Do all post requests
smps++;
pReq = (POOL_DATA_BLOCK*)pReq->Block.Next;
} // Post all smps
// Add Smp block to global list
// Lock SmpList
// unlock block
// If only local send do a manual recv completions
if ( TRUE == bLocal )
{
SmaCompletionCallback( NULL );
}
}
exit:
_DBG_LEAVE_LVL(_DBG_LVL_SEND);
return status;
}
//
// SmaPostSmp
//
// All SMPs Sent on the wire go through this call. This call is used by
// SmaPostSend() and Internal Callback reply sends as well as directed route
// sends
//
// INPUTS:
//
// CaObj -CA on which the SMP goes out
// Req -The SMP request being sent out
// CqTrigger -Do a ream of CQ if TRUE
//
//
// OUTPUTS:
//
//
//
// RETURNS:
//
//
// IRQL:
//
//
FSTATUS
SmaPostSmp(
IN SMA_CA_OBJ_PRIVATE *CaObj,
IN POOL_DATA_BLOCK *Req
)
{
FSTATUS status;
IB_WORK_REQ *workRequest;
CA_MEM_LIST caMemList;
SMA_PORT_TABLE_PRIV *pPortTbl;
STL_SMP *pSmp;
IB_WORK_REQ wrq;
IB_LOCAL_DATASEGMENT dsList[1];
IB_ADDRESS_VECTOR avInfo;
uint8 portNo;
_DBG_ENTER_LVL ( _DBG_LVL_SEND, SmaPostSmp );
MemoryClear(&wrq, sizeof(wrq));
workRequest = &wrq;
workRequest->DSList = &dsList[0];
pSmp = (STL_SMP*)Req->Block.Smp;
#ifdef IB_DEBUG
//
// Trap Debug Info
//
if ( MMTHD_TRAP == pSmp->common.mr.s.Method )
{
BSWAP_MAD_HEADER( (MAD*)pSmp);
BSWAP_IB_NOTICE(((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData)));
#ifdef ICS_LOGGING
_DBG_PRINT(_DBG_LVL_CALLBACK, (
">>>>Trap Info:>>>>>\n"
"\tAttribModifier....: %x\n"
"\tStatus............: %x\n"
"\tIsGenericBit......: %x\n"
"\tTrapNumber........: %x\n"
"\tProducerType......: %x\n"
"\tType..............: %x\n",
pSmp->common.AttributeModifier,
pSmp->common.u.NS.Status.AsReg16,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->u.Generic.u.s.IsGeneric,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->u.Generic.TrapNumber,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->u.Generic.u.s.ProducerType,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->u.Generic.u.s.Type
));
_DBG_PRINT(_DBG_LVL_CALLBACK, (
">>>>Trap Info (cont):>>>>>\n"
"\tIssuerLID.........: %x\n"
"\tNoticeCount.......: %x\n"
"\tNoticeToggle......: %x\n"
"\tdata..............: %x\n",
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->IssuerLID,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->Stats.Count,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->Stats.Toggle,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->Data[0]
));
#else
_DBG_PRINT (_DBG_LVL_CALLBACK, (
">>>>Trap Info:>>>>>\n"
"\tAttribModifier....: %x\n"
"\tStatus............: %x\n"
"\tIsGenericBit......: %x\n"
"\tTrapNumber........: %x\n"
"\tProducerType......: %x\n"
"\tType..............: %x\n"
"\tIssuerLID.........: %x\n"
"\tNoticeCount.......: %x\n"
"\tNoticeToggle......: %x\n"
"\tdata..............: %x\n",
pSmp->common.AttributeModifier,
pSmp->common.u.NS.Status.AsReg16,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->u.Generic.u.s.IsGeneric,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->u.Generic.TrapNumber,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->u.Generic.u.s.ProducerType,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->u.Generic.u.s.Type,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->IssuerLID,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->Stats.Count,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->Stats.Toggle,
((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData))->Data[0]
));
#endif
#undef ProducerType
BSWAP_IB_NOTICE(((IB_NOTICE *)(pSmp->SmpExt.LIDRouted.SMPData)));
BSWAP_MAD_HEADER( (MAD*)pSmp);
} // trap info
#endif /* IB_DEBUG */
//
// get end-point info
//
pPortTbl = (SMA_PORT_TABLE_PRIV *)CaObj->CaPublic.PortTbl;
if (CaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0)
{
portNo = 0;
}
else
{
portNo = Req->Block.PortNumber - 1;
}
avInfo.PortGUID = pPortTbl->PortBlock[portNo].Public.GUID;
avInfo.DestLID = Req->Block.DLID;
avInfo.PathBits = Req->Block.PathBits;
avInfo.ServiceLevel = Req->Block.ServiceLevel;
avInfo.StaticRate = Req->Block.StaticRate;
avInfo.GlobalRoute = FALSE;
avInfo.GlobalRouteInfo.FlowLabel = 0;
status = GetAV(
CaObj,
&avInfo,
&Req->AvHandle );
if ( FSUCCESS == status )
{
//
// prepare the request
//
workRequest->Operation = WROpSend;
workRequest->DSList[0].Address = (uintn)Req->Block.Smp;
workRequest->DSList[0].Length = Req->Block.SmpByteCount;
GetMemInfoFromCaMemTable(
CaObj,
Req->CaMemIndex,
&caMemList);
workRequest->DSList[0].Lkey = caMemList.LKey;
workRequest->DSListDepth = 1;
workRequest->MessageLen = Req->Block.SmpByteCount;
workRequest->Req.SendUD.AVHandle= Req->AvHandle;
//
//workRequest->Req.SendUD.ImmediateData = 0xdeadbeef;
//
workRequest->Req.SendUD.Options.AsUINT16 = 0;
workRequest->Req.SendUD.Options.s.SignaledCompletion= 1;
#if INCLUDE_16B
workRequest->Req.SendUD.Options.s.SendFMH = 0;
#endif
//
// Shall be set to zero for SMPs!
//
workRequest->Req.SendUD.QPNumber = 0;
workRequest->Req.SendUD.Qkey = 0;
//
// set Id to request stucture to unload
//
ASSERT_VALID_WORKREQID((uintn)Req);
workRequest->WorkReqId = BUILD_SQ_WORKREQID((uintn)Req);
CaObj->WorkReqId++;
//
// Byte Order MAD Commen Header
//
BSWAP_STL_SMP_HEADER ((STL_SMP*)Req->Block.Smp);
#if 0
//
// yank request sends from pre-existing blocks.
// if they do not exist, create one. if the requirement
// is high, the new creation will satisfy the request
//WorkReq = SmObject->WorkReqSend;
//
if ( TRUE == Req->CQTrigger )
{
//
// The next Work Completion written to the
// CQ will generate an event
//
status = iba_rearm_cq(
CaObj->CqHandle,
CQEventSelNextWC
);
}
#endif
SpinLockAcquire( &pPortTbl[portNo].PortBlock->Qp0Lock );
status = iba_post_send(
pPortTbl[portNo].PortBlock->QpHandle,
workRequest );
SpinLockRelease( &pPortTbl[portNo].PortBlock->Qp0Lock );
}
else
{
_DBG_ERROR ((
"AV error on LID = x%x! \n",
avInfo.DestLID ));
} // AV handle
if ( FSUCCESS != status )
{
Req->Block.Status = status;
}
_DBG_LEAVE_LVL (_DBG_LVL_SEND);
return status;
}