/* 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"


//
// SmaProcessLocalToSma
//
//	Process Local SMP activity to SMA.
//	Does a local Operation and sets internal states
//
//
//
// INPUTS:
//
//	SmpBlock		- SMP Block received
//
// OUTPUTS:
//
//	TRUE/FALSE		- TRUE: SMP processed and used
//					- FALSE: Recycle SMP
//
// RETURNS:
//
//	FSTATUS.
//
// IRQL:
//
//	This routine is called at IRQL_DISPATCH_LEVEL.
//

static boolean
SmaProcessLocalToSma(
	IN	POOL_DATA_BLOCK		*SmpBlock
	)
{
	FSTATUS					status = FSUCCESS;
	STL_SMP					*pSmp = (STL_SMP*)SmpBlock->Block.Smp;
	SMA_CA_OBJ_PRIVATE		*pCaObj;
	SMA_PORT_TABLE_PRIV		*pPortTbl;
	SMA_PORT_BLOCK_PRIV		*pPortBlock;
	IB_PORT_STATE			oldPortState;
	boolean					bStatus=TRUE;		// default processed
	uint8					portNo,oldLMC;
	IB_LID					oldLID, oldSmLID;
	uint8					method;
	boolean					zero_lid;	

	_DBG_ENTER_LVL( _DBG_LVL_CALLBACK, SmaProcessLocalToSma );

	pCaObj = (SMA_CA_OBJ_PRIVATE *)SmpBlock->Block.SmaCaContext;
	pPortTbl = (SMA_PORT_TABLE_PRIV *)pCaObj->CaPublic.PortTbl;

	/* special case, suppress INVALID_ATTRIB error message on Set(PortInfo)
	 * with 0 LID.  This can occur for host SM when 1st starting only SM
	 * on fabric.  What happens is SM sets capability mask using present LID
	 * which is 0 when HFI first comes up.
	 */
	zero_lid = (pSmp->common.mr.AsReg8 == MMTHD_SET
		&& pSmp->common.AttributeID == STL_MCLASS_ATTRIB_ID_PORT_INFO
		&& 0 == ((STL_PORT_INFO *)&pSmp->SmpExt.DirectedRoute.SMPData)->LID);

	// save method
	method = pSmp->common.mr.AsReg8;
	// clear Status
	pSmp->common.u.DR.s.Status = MAD_STATUS_SUCCESS;

	if (pCaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0)
	{
		if (SmpBlock->Block.PortNumber != 0)
		{
			_DBG_ERROR(("SmpBlock->Block.PortNumber == %d, should be 0!\n", SmpBlock->Block.PortNumber));
			status = FERROR;
		}
		portNo = 0;
	} else {
		portNo = SmpBlock->Block.PortNumber - 1;
	}

	// call verb
	if (FSUCCESS == status)
	{
		status = iba_get_set_mad(
					pPortTbl->PortBlock[portNo].QpHandle,
					SmpBlock->Block.PortNumber,
					SmpBlock->Block.SLID,
					pSmp,
					&SmpBlock->Block.SmpByteCount);
	}

	if ( FSUCCESS != status )
	{
		// FNOT_DONE indicates an internal MAD which we chose to quietly discard
		if (FNOT_DONE != status)
		{
			_DBG_ERROR(("GetSetMad() failed with x%x!\n", status));
		}

		// return VPD reported status in status
		SmpBlock->Block.Status = status;
		bStatus = FALSE;
	}
	// we need to update port state view even if Mad returns
	// INVALID_ATTRIB valid because it is reporting the new state
	else if (pSmp->common.u.DR.s.Status == MAD_STATUS_SUCCESS
		|| pSmp->common.u.DR.s.Status == MAD_STATUS_INVALID_ATTRIB)
	{
		// Update internal states 
		switch ( pSmp->common.AttributeID )
		{
		default:
			{
				break;
			}

		case	MCLASS_ATTRIB_ID_PORT_INFO:
			{
				// Update internal port state: LID, LMC, State
				if (pCaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0)
				{
					if (pSmp->common.AttributeModifier != 0)
					{
						// Nothing to cache for switch ports other than 0
						break;
					}
					portNo = 0;	// Switch port 0 is treated as first 0 in list
				} else {
					portNo = (uint8)(pSmp->common.AttributeModifier & 0x000000FF);
					if (0 == portNo)	// Is it the default port?
					{
						portNo = SmpBlock->Block.PortNumber - 1;	// use in port
					} else {
						portNo--;		// get zero based index into port tables
	
						// Check for invalid port number, GetSetMad should have
						// already checked, so we do not expect the test below
						// to fail.
						if ( portNo >= pCaObj->CaPublic.Ports )
						{
							_DBG_ERROR(("Invalid Port: %d\n", portNo+1));
							bStatus = FALSE;		// SMP not processed
						}
					}
				}

				if (bStatus)
				{
					STL_PORT_INFO *pPortInfo =
						(STL_PORT_INFO *)&pSmp->SmpExt.DirectedRoute.SMPData;
					BSWAP_STL_PORT_INFO(pPortInfo);
					ZERO_RSVD_STL_PORT_INFO(pPortInfo);
					pPortBlock = &pPortTbl->PortBlock[portNo];
					
					// Before we update our internal state save 
					// old state for callbacks
					oldPortState = pPortBlock->Public.State;
					oldLID = pPortBlock->Public.Address.BaseLID;
					oldLMC = pPortBlock->Public.Address.LMC;
					oldSmLID = pPortBlock->SMLID;

					// Update internal state
					pPortBlock->Public.Address.BaseLID = pPortInfo->LID;
					pPortBlock->Public.Address.LMC = pPortInfo->s1.LMC;
					pPortBlock->Public.State = (IB_PORT_STATE) pPortInfo->PortStates.s.PortState;
					pPortBlock->SMLID = pPortInfo->MasterSMLID;

					// Notify users of a Port event
					if ( oldPortState != pPortBlock->Public.State )
					{
						if ( IB_PORT_ACTIVE == pPortBlock->Public.State )
						{
							IbtNotifyGroup( pPortBlock->Public.GUID,
												IB_NOTIFY_LID_EVENT );	
						}

						// Here any other state will be a port down event
						// An SM could change a port status from Active to 
						// Armed to reconfigure it. We take care of a case
						// where connections were active and go down on an
						// incoming ARM Smp that needs to report to Channel
						// drivers that the port is down.
						if (( IB_PORT_DOWN == pPortBlock->Public.State ) ||\
							( IB_PORT_ACTIVE == oldPortState ))
						{
							IbtNotifyGroup( pPortBlock->Public.GUID,
												IB_NOTIFY_PORT_DOWN );	
						}
					} else {
						// Notify changes to Active LID/LMC when multipathing
						// kicks in or on a SM takeover.
						if ( IB_PORT_ACTIVE == pPortBlock->Public.State ) 
						{
							if (( oldLID != pPortInfo->LID ) || \
								( oldLMC != pPortInfo->s1.LMC ))
							{
								IbtNotifyGroup( pPortBlock->Public.GUID,
												IB_NOTIFY_LID_EVENT );	
							} else if ( oldSmLID != pPortInfo->MasterSMLID 
								 || (method == MMTHD_SET && pPortInfo->Subnet.ClientReregister) )
							{
								IbtNotifyGroup( pPortBlock->Public.GUID,
												IB_NOTIFY_SM_EVENT );	
							}
						}
					}
					BSWAP_STL_PORT_INFO(pPortInfo);
				} // valid portNo

				break;
			}

		case	STL_MCLASS_ATTRIB_ID_PART_TABLE:
			if (bStatus && method == MMTHD_SET)
			{
				// Notify users of PKey event
				if (pCaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0)
				{
					portNo = (pSmp->common.AttributeModifier >> 16) & 0x00FF;
					if (portNo != 0)
					{
						break;
					}
				} else {
					portNo = SmpBlock->Block.PortNumber - 1;	// use in port
				}
				
				pPortBlock = &pPortTbl->PortBlock[portNo];

				// BUGBUG TBD compare PKeys and only notify if they changed
				IbtNotifyGroup( pPortBlock->Public.GUID, IB_NOTIFY_PKEY_EVENT);
			}

			break;

		}	// switch

		if (pSmp->common.u.DR.s.Status == MAD_STATUS_INVALID_ATTRIB
			&& ! zero_lid)
		{
			_DBG_ERROR(("GetSetMad() returned MAD with Status x%x!\n", 
					pSmp->common.u.DR.s.Status));
		}
	} else {
		_DBG_ERROR(("GetSetMad() returned MAD with Status x%x!\n", 
					pSmp->common.u.DR.s.Status));
	}		// GetSetMad status

	_DBG_RETURN_LVL(_DBG_LVL_CALLBACK, bStatus);

	return (bStatus);
}

//
// SmaProcessSmaSmpRcvd
//
//	Processes SMA SMP received.
//	A check is done to verify the validity of the SMP.
//	If the SMP needs further processing like a reply, this returns TRUE
//	and the caller must ProcessSmiSmp and if appropriate
//	post a reply on the SendQ of this port's QP.
//
//
// INPUTS:
//
//	SmpBlock		- SMP Block received
//
// OUTPUTS:
//
//	TRUE/FALSE		- TRUE: SMP processed and used
//					- FALSE: Recycle SMP
//
// RETURNS:
//
//	FSTATUS.
//
// IRQL:
//
//	This routine is called at IRQL_DISPATCH_LEVEL.
//

boolean
SmaProcessSmaSmpRcvd(
	IN	POOL_DATA_BLOCK		*SmpBlock
	)
{
	STL_SMP					*pSmp = (STL_SMP*)SmpBlock->Block.Smp;
	SMA_CA_OBJ_PRIVATE		*pCaObj;
	SMA_PORT_TABLE_PRIV		*pPortTbl;
	boolean					bProcess=TRUE;


	_DBG_ENTER_LVL(_DBG_LVL_CALLBACK, SmaProcessSmaSmpRcvd);

	pCaObj = (SMA_CA_OBJ_PRIVATE *)SmpBlock->Block.SmaCaContext;
	pPortTbl = (SMA_PORT_TABLE_PRIV *)pCaObj->CaPublic.PortTbl;


	// M_Key check is expected to be done by SmaProcessLocalToSma
	// it should keep track of the M_Key for each port and validate them

	bProcess = SmaProcessLocalToSma( SmpBlock );
	if ( TRUE == bProcess )
	{

		//
		// Return info :
		// Set HopPointer HopCount info
		//
		
		//
		// DLID = SLID of request
		//

		if (SmpBlock->Block.SLID == 0)
		{
			// special case for locally generated Traps provided to us on
			// QP0 by firmware.  We send to SM.
			if ( MCLASS_SM_DIRECTED_ROUTE == pSmp->common.MgmtClass )
			{
				SmpBlock->Block.SLID = STL_LID_PERMISSIVE;
				SmpBlock->Block.DLID = STL_LID_PERMISSIVE;
				SmpBlock->Block.PathBits = 0;
			} else {
				SmpBlock->Block.DLID = pPortTbl[SmpBlock->Block.PortNumber - 1].PortBlock->SMLID;
				SmpBlock->Block.PathBits = 0;	// use our first lid
				SmpBlock->Block.SLID = 0;		// will fill in our lid below
			}
		} else {
			// IBTA 1.1 14.2.2.3 C14-10
			// GetResp is set by GetSetMad for SMA packets
			// D bit is set by GetSetMad for Directed Route SMA packets
			// GetSetMad uses original SMP buffer, so other parts of Route
			// are copied as is
			// C14-11 2nd bullet
			SmpBlock->Block.DLID = SmpBlock->Block.SLID;
			SmpBlock->Block.SLID = 0;		// will fill in our lid below
			// PathBits of SLID will be from DestPathBits in request
		}

		if ( MCLASS_SM_DIRECTED_ROUTE == pSmp->common.MgmtClass )
		{
			if ( STL_LID_PERMISSIVE == pSmp->SmpExt.DirectedRoute.DrDLID) {
				// IBTA 1.1 Sec 14.2.2.3 C14-11
				// the directed route part starts from the respondor node (us)
				// LRH_SLID shall be the permissive lid or a lid of this port
				// safest to use permissive lid, since we may not have a lid yet
				SmpBlock->Block.SLID = STL_LID_PERMISSIVE;
			}
		}

		//
		// If ( SLID != LID_PERMISSIVE )
		//		SLID = Responder's LID
		//

		if ( STL_LID_PERMISSIVE != SmpBlock->Block.SLID )
		{
			uint8 lmc = pPortTbl[SmpBlock->Block.PortNumber - 1].PortBlock->Public.Address.LMC;
			SmpBlock->Block.SLID = 
				PathBitsToLid(pPortTbl[SmpBlock->Block.PortNumber - 1].PortBlock->Public.Address.BaseLID,
						SmpBlock->Block.PathBits, lmc);
			_DBG_PRINT (_DBG_LVL_CALLBACK, (
					"Response lmc: %d\n"
					"Pathbits: %d\n"
					"SLID: %x\n",
					lmc,
					SmpBlock->Block.PathBits,
					SmpBlock->Block.SLID));
		}
	}

	_DBG_RETURN_LVL(_DBG_LVL_CALLBACK, bProcess);

	return (bProcess);
 
}
	
// Per SMI packet processing as defined in section 13.5.3.3 and 14.2.2
// of IBTA 1.1
// This should be used to process all SMP packets arriving at the SMI
// both from QP0, responses from the local SMA, and packets from SmaPostSend
// 
// INPUTS:
//
//	pCaObj			- CA receiving block
//	SmpBlock		- SMP Block delivered to Smi
//	FromWire		- was SMP received on QP0 or delivered locally to SMI
//						affects LID routed identification
//
// RETURNS:
// 	SMI_DISCARD - SMP should be discarded
// 	SMI_SEND - SMP should be sent on the wire via PortNumber in pSmpBlock
// 	SMI_TO_LOCAL_SM - SMP should be delivered to local SM
// 	SMI_TO_LOCAL - SMP should be delivered to local SM or SMA
// 				see SmaProcessLocalSmp
// 	SMI_LID_ROUTED - LID routed SMP, destination will depend on context of call
SMI_ACTION
SmaProcessSmiSmp(
	IN	SMA_CA_OBJ_PRIVATE	*pCaObj,
	IN OUT POOL_DATA_BLOCK	*pSmpBlock,
	IN	boolean				FromWire
	)
{
	IN STL_SMP			*pSmp     = (STL_SMP*)pSmpBlock->Block.Smp;
	SMA_PORT_TABLE_PRIV	*pPortTbl = (SMA_PORT_TABLE_PRIV *)pCaObj->CaPublic.PortTbl;

	_DBG_ENTER_LVL(_DBG_LVL_CALLBACK, SmaProcessSmiSmp);

	_DBG_PRINT (_DBG_LVL_CALLBACK, (
			"SMI Processing for:\n"
			"\tClass........:x%x\n"
			"\tAttribID.....:x%X\n"
			"\tAttribModfier:x%X\n"
			"\tMethod.......:x%X\n"
			"\tResponseBit..:x%X\n"
			"\tHopCount.....:x%X\n",
			pSmp->common.MgmtClass,
			pSmp->common.AttributeID,
			pSmp->common.AttributeModifier,
			pSmp->common.mr.s.Method,
			pSmp->common.mr.s.R,
			pSmp->common.u.DR.HopCount
			));
	_DBG_PRINT (_DBG_LVL_CALLBACK, (
			"SMI Processing for (cont):\n"
			"\tHopPointer...:x%X\n"
			"\tDrSLID.......:x%X\n"
			"\tDrDLID.......:x%X\n"
			"\tTID..........:x%"PRIx64"\n"
			"\tSLID.........:x%X\n"
			"\tDLID.........:x%X\n",
			pSmp->common.u.DR.HopPointer,
			pSmp->SmpExt.DirectedRoute.DrSLID,
			pSmp->SmpExt.DirectedRoute.DrDLID,
			pSmp->common.TransactionID,
			pSmpBlock->Block.SLID,
			pSmpBlock->Block.DLID
			));

	// Algorithm for SMP handling per IBTA 1.1 Section 13.5.3.3
	// Rather than be elegant, the flow charts are followed fairly
	// directly, allowing for easier code review and support.
	// However, there are cases where the flow charts are in error
	// in which case the text in 14.2.2 takes precedence.
	// Assumes LRH, BTH and GRH checks are done in the Verbs
	// Provider Driver
				
	// Figure 157, SMP Check 1
	if (pSmp->common.BaseVersion != STL_BASE_VERSION
		|| pSmp->common.ClassVersion != STL_SM_CLASS_VERSION)
	{
		// can optionally send a GetResp(MAD_STATUS_UNSUPPORTED_VER)
		// however since we don't understand the header we could
		// not effectively respond to directed route
		// so better to discard
        _DBG_PRINT (_DBG_LVL_CALLBACK,(
                    "Unsupported Base or Class Version [packet discarded]\n"));
		goto discard;
	}
	if( pSmp->common.MgmtClass != MCLASS_SM_DIRECTED_ROUTE
		&& pSmp->common.MgmtClass != MCLASS_SM_LID_ROUTED ) 
	{
		// drop packet, unknown class
        _DBG_PRINT (_DBG_LVL_CALLBACK,(
                    "Invalid Mgmt Class on SMI [packet discarded]\n"));
		goto discard;
	}

	if( pSmp->common.MgmtClass != MCLASS_SM_DIRECTED_ROUTE ) 
	{
		// LID routed, next check is MKey which is done in Verbs Driver/SMA
        _DBG_PRINT (_DBG_LVL_CALLBACK,("LID routed SMP\n"));
		goto lidsmp;
	}

	// not in spec, but a necessary check so indexing
	// based on HopPointer below doesn't overflow
	if (pSmp->common.u.DR.HopCount >
			sizeof(pSmp->SmpExt.DirectedRoute.InitPath)
		|| pSmp->common.u.DR.HopPointer > 
			sizeof(pSmp->SmpExt.DirectedRoute.InitPath))
	{
		_DBG_ERROR((
			"HopPointer(%d) or HopCount(%d) exceeds array bounds!\n"
			"Check your SMP values [packet discarded]\n",
			pSmp->common.u.DR.HopPointer,
			pSmp->common.u.DR.HopCount
			));
    
		goto discard;
	}

	// Figure 161, SMP Direct Route Check 1
	if ( 0 == pSmp->common.u.DR.s.D)
	{
		if ( ! FromWire && STL_LID_PERMISSIVE != pSmp->SmpExt.DirectedRoute.DrSLID)
		{
			// IBTA 1.1 sec 14.2.2.1
			// For packets originated by a local SM
			// if Directed Route Part does not start from requestor node
			// process as LID routed
			goto sendsmp;
		}
		// 14.2.2.2, C14-8, C14-9
		if (0 != pSmp->common.u.DR.HopCount
			&& 0 == pSmp->common.u.DR.HopPointer)
		{
			// SMP is at beginning of directed route
			// C14-9 clause 1)
			++(pSmp->common.u.DR.HopPointer);
			pSmpBlock->Block.SLID = STL_LID_PERMISSIVE;
			// flow chart Figure 161 disagrees with text, Text has priority
			//pSmpBlock->Block.DLID = pSmp->SmpExt.DirectedRoute.DrDLID;
			pSmpBlock->Block.DLID = STL_LID_PERMISSIVE;
			pSmpBlock->Block.PortNumber = pSmp->SmpExt.DirectedRoute.InitPath[pSmp->common.u.DR.HopPointer];
			goto sendsmp;
		}
		if (0 != pSmp->common.u.DR.HopCount
			&& 1 <= pSmp->common.u.DR.HopPointer
			&& pSmp->common.u.DR.HopPointer <
			pSmp->common.u.DR.HopCount
			)
		{
			// SMP is at intermediate hop in directed route portion of path
			// C14-9 clause 2)
			if (pCaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0)
			{
				// Switch code here
				pSmp->SmpExt.DirectedRoute.RetPath[pSmp->common.u.DR.HopPointer] = pSmpBlock->Block.PortNumber;
				++pSmp->common.u.DR.HopPointer;
				pSmpBlock->Block.SLID = STL_LID_PERMISSIVE;
				pSmpBlock->Block.DLID = STL_LID_PERMISSIVE;
				pSmpBlock->Block.PortNumber = pSmp->SmpExt.DirectedRoute.InitPath[pSmp->common.u.DR.HopPointer];
				goto sendsmp;
			}
			else
			{
				// we are not a switch so we drop
				_DBG_PRINT (_DBG_LVL_CALLBACK,(
						"Invalid DR [packet discarded per Check 1]\n"));
				goto discard;
			}
		}
		// Figure 162 SMP Direct Route Check 2
		if (pSmp->common.u.DR.HopCount
			== pSmp->common.u.DR.HopPointer)
		{
			// SMP is at end of directed route portion of path
			// C14-9 clause 3)
			if (0 != pSmp->common.u.DR.HopCount)
			{
				pSmp->SmpExt.DirectedRoute.RetPath[pSmp->common.u.DR.HopPointer] = pSmpBlock->Block.PortNumber;
			}
			++(pSmp->common.u.DR.HopPointer);

			if (pCaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0)
			{
				if (STL_LID_PERMISSIVE == pSmp->SmpExt.DirectedRoute.DrDLID)
				{
					pSmpBlock->Block.SLID = STL_LID_PERMISSIVE;
				}
				else
				{
					pSmpBlock->Block.SLID = pPortTbl[0].PortBlock->Public.Address.BaseLID |
											pSmpBlock->Block.PathBits;
				}
			}
			else
			{
				// not a switch
				if ( STL_LID_PERMISSIVE != pSmp->SmpExt.DirectedRoute.DrDLID)
				{
					_DBG_PRINT (_DBG_LVL_CALLBACK,(
						"Invalid DR [packet discarded per Check 2]\n"));
					goto discard;
				}
				//pSmpBlock->Block.SLID = LID_PERMISSIVE;	// optional in spec
			}

			pSmpBlock->Block.DLID = pSmp->SmpExt.DirectedRoute.DrDLID;
			// Text in clause 3 has this additional test which is
			// redundant with test above due to DLID assignment above
			// for a CA DLID will be PERMISSIVE if get here
			if ( STL_LID_PERMISSIVE == pSmpBlock->Block.DLID )
			{
				// per path above, HopPointer is now HopCount+1
#ifdef  IB_DEBUG
				ASSERT(pSmp->common.u.DR.HopCount+1 == pSmp->common.u.DR.HopPointer);
#endif
				goto localsmp;
			} else {
#ifdef  IB_DEBUG
				if (!(pCaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0))
				{
					// can't get here given sequence of tests above for a CA
					ASSERT(0);
				}
#endif
				goto sendsmp;
			}
		} else if ( pSmp->common.u.DR.HopPointer == pSmp->common.u.DR.HopCount+1 )
		{
			// SMP is a local request to SM or SMA
			// C14-9 clause 4)
			goto localsmp;
		} else {
			// HopPointer is in range HopCount+1 <HopPointer <=255
			// C14-9 clause 5)
        	_DBG_PRINT (_DBG_LVL_CALLBACK,(
                    "Invalid DR [packet discarded per Check 3]\n"));
			goto discard;
		}
	} else { // D bit == 1 case of below
		if ( ! FromWire && (STL_LID_PERMISSIVE != pSmp->SmpExt.DirectedRoute.DrDLID))
		{
			// IBTA 1.1 sec 14.2.2.3
			// For packets originated by a local SMA or SM
			// if Directed Route Part does not start from responder node
			// process as LID routed
			goto sendsmp;
		}
		// Figure 164 SMP Direct Route Check 4
		if (0 != pSmp->common.u.DR.HopCount
			&& pSmp->common.u.DR.HopPointer ==
				pSmp->common.u.DR.HopCount+1
			)
		{
			// SMP is at beginning of directed route
			// C14-13 clause 1)
			--(pSmp->common.u.DR.HopPointer);
			pSmpBlock->Block.SLID = STL_LID_PERMISSIVE;
			pSmpBlock->Block.DLID = pSmp->SmpExt.DirectedRoute.DrDLID;
			pSmpBlock->Block.PortNumber = pSmp->SmpExt.DirectedRoute.RetPath[pSmp->common.u.DR.HopPointer];
			goto sendsmp;
		} else if (0 != pSmp->common.u.DR.HopCount
					&& 2 <= pSmp->common.u.DR.HopPointer
					&& pSmp->common.u.DR.HopPointer <=
							pSmp->common.u.DR.HopCount
					)
		{
			// SMP is at an intermediate hop of directed route
			// C14-13 clause 2)
			if (pCaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0)
			{
				--pSmp->common.u.DR.HopPointer;
				pSmpBlock->Block.SLID = STL_LID_PERMISSIVE;
				pSmpBlock->Block.DLID = STL_LID_PERMISSIVE;
				pSmpBlock->Block.PortNumber = pSmp->SmpExt.DirectedRoute.RetPath[pSmp->common.u.DR.HopPointer];
			}
			else
			{
				// we are not a switch so we drop
				_DBG_PRINT (_DBG_LVL_CALLBACK,(
						"Invalid DR [packet discarded per Check 4]\n"));
				goto discard;
			}
		}
		// Figure 165 SMP Direct Route Check 5
		if ( 1 == pSmp->common.u.DR.HopPointer)
		{
			// SMP is at end of directed route part of path
			// C14-13 clause 3)
			--(pSmp->common.u.DR.HopPointer);
			if (pCaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0)
			{
				if (STL_LID_PERMISSIVE == pSmp->SmpExt.DirectedRoute.DrSLID)
				{
					pSmpBlock->Block.SLID = STL_LID_PERMISSIVE;
				}
				else
				{
					pSmpBlock->Block.SLID = pPortTbl[0].PortBlock->Public.Address.BaseLID |
											pSmpBlock->Block.PathBits;
				}
			}
			else
			{
				// we are not a switch
				if ( STL_LID_PERMISSIVE != pSmp->SmpExt.DirectedRoute.DrSLID)
				{
					_DBG_PRINT (_DBG_LVL_CALLBACK,(
						"Invalid DR [packet discarded per Check 5]\n"));
					goto discard;
				}
				pSmpBlock->Block.SLID = STL_LID_PERMISSIVE;
			}

			pSmpBlock->Block.DLID = pSmp->SmpExt.DirectedRoute.DrSLID;
			// Text in clause 3 has this additional test which is
			// redundant with test above due to DLID assignment above
			// for a CA DLID will be PERMISSIVE if get here
			if ( STL_LID_PERMISSIVE == pSmpBlock->Block.DLID )
			{
				// per path above, HopPointer is now HopCount+1
#ifdef IB_DEBUG
				ASSERT(pSmp->common.u.DR.HopPointer == 0);
#endif
				goto localsm;
			} else {
#ifdef  IB_DEBUG
				if (!(pCaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0))
				{
					// can't get here given sequence of tests above for a CA
					ASSERT(0);
				}
#endif
				goto sendsmp;
			}
			// Figure 166, SMP - Direct Route Check 6
		} else if ( 0 == pSmp->common.u.DR.HopPointer)
		{
			// SMP is to a local SM
			// C14-13 clause 4)
			goto localsm;
		} else {
			// means HopCount+2 <= HopPointer <= 255
        	_DBG_PRINT (_DBG_LVL_CALLBACK,(
                   "Invalid DR [packet discarded per Check 6]\n"));
			goto discard;
		}
	}

lidsmp:
    _DBG_RETURN_LVL(_DBG_LVL_CALLBACK, SMI_LID_ROUTED);
	return SMI_LID_ROUTED;

localsmp:
    _DBG_RETURN_LVL(_DBG_LVL_CALLBACK, SMI_TO_LOCAL);
	return SMI_TO_LOCAL;

localsm:
    _DBG_RETURN_LVL(_DBG_LVL_CALLBACK, SMI_TO_LOCAL_SM);
	return SMI_TO_LOCAL_SM;

sendsmp:
	// validate output port number
	// With the switch's built in SMA, we leave validation to the switch firmware
	// On the host we handle it here
	if (!(pCaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0) &&
		(pSmpBlock->Block.PortNumber < 1 || pSmpBlock->Block.PortNumber > pCaObj->CaPublic.Ports))
	{
		_DBG_PRINT (_DBG_LVL_CALLBACK,(
					"Invalid DR Port Number [packet discarded]\n"));
		goto discard;
	}

	_DBG_RETURN_LVL(_DBG_LVL_CALLBACK, SMI_SEND);
	return SMI_SEND;

discard:
    _DBG_RETURN_LVL(_DBG_LVL_CALLBACK, SMI_DISCARD);
	return SMI_DISCARD;
}

// Process an SMP which is routed to a local SMA or SM
// returns action to be performed with SmpBlock
// SMI_TO_LOCAL_SM - pass on to SM
// SMI_DISCARD - discard packet
// SMI_SEND - Smp should be sent on the wire
// SMI_TO_LOCAL - never returned by this function
// SMI_LID_ROUTED - LID routed SMP to SMA, send response back to source
SMI_ACTION
SmaProcessLocalSmp(
	IN	SMA_CA_OBJ_PRIVATE	*pCaObj,
	IN	POOL_DATA_BLOCK		*pSmpBlock
	)
{
	STL_SMP			*pSmp = (STL_SMP*)pSmpBlock->Block.Smp;

	_DBG_ENTER_LVL(_DBG_LVL_CALLBACK, SmaProcessLocalSmp);
	// M_Key checks are done in GetSetMad, so we defer
	// IBTA 1.1 Figure 158 SMP M_Key Check until we give to SMA
				
	// Sma Processes local Smps for the following:
	//	1) It a a request and not a response
	//	2) It is not a Trap (Trap does not have R bit set)
	//	3) It is not a SM_INFO

	// This is based on IBTA 1.1 Figure 159 SMP Check 2
	// with some minor changes to the flow

	if (( MMTHD_TRAP == pSmp->common.mr.s.Method)
		|| ( MCLASS_ATTRIB_ID_SM_INFO == pSmp->common.AttributeID )
		|| ( 0 != pSmp->common.mr.s.R ) 
		)
	{
		// Traps, SM_INFO and Responses are destined to SM
		// not destined to SMA, just pass to SM if any
        _DBG_PRINT (_DBG_LVL_CALLBACK,(
                    "Trap, SMInfo or Response to SM\n"));
    	_DBG_RETURN_LVL(_DBG_LVL_CALLBACK, SMI_TO_LOCAL_SM);
		return SMI_TO_LOCAL_SM;
	}

	if ((MMTHD_GET == pSmp->common.mr.s.Method)
		|| (MMTHD_SET == pSmp->common.mr.s.Method)
		|| (MMTHD_TRAP_REPRESS == pSmp->common.mr.s.Method))
	{
		SMI_ACTION SmiAction;

		// destined to SMA
		if ( FALSE == SmaProcessSmaSmpRcvd(pSmpBlock))
		{
			return SMI_DISCARD;
		}
		SmiAction = SmaProcessSmiSmp(pCaObj, pSmpBlock, FALSE);

		if (SMI_TO_LOCAL == SmiAction)
		{
			return SMI_TO_LOCAL_SM;
		}
    	_DBG_RETURN_LVL(_DBG_LVL_CALLBACK, SmiAction);
		return SmiAction;
	} else {
		// unexpected method - discard
        _DBG_PRINT (_DBG_LVL_CALLBACK,(
                    "Unexpected Method [packet discarded]\n"));
    	_DBG_RETURN_LVL(_DBG_LVL_CALLBACK, SMI_DISCARD);
		return SMI_DISCARD;
	}
}

// Add to list of completed Sends to be reported to SM
void
SmaAddToSendQ(
	IN	POOL_DATA_BLOCK		*SmpBlock
	)
{
	FSTATUS					status;
	SMP_LIST				*pSmpList;
	STL_SMP					*pSmp;
	
	//
	// first free AV handle
	//
	if( NULL != SmpBlock->AvHandle )
	{
		status = PutAV(
					(SMA_CA_OBJ_PRIVATE*)SmpBlock->Block.SmaCaContext,
					SmpBlock->AvHandle);
		SmpBlock->AvHandle = 0;
	}

	pSmpList = (SMP_LIST*)SmpBlock->Base;

	if ( NULL != pSmpList )
	{
		//
		// Increment recvd counter
		//
		AtomicIncrementVoid( &pSmpList->SmpsOut );


		// set error indicator.
		if ( FSUCCESS != SmpBlock->Block.Status )
			pSmpList->SmpsInError = 1;

		//
		// Byte order for user
		//
		BSWAP_MAD_HEADER( (MAD*)SmpBlock->Block.Smp);

		pSmp = (STL_SMP*)SmpBlock->Block.Smp;

#ifdef  ICS_LOGGING
		_DBG_PRINT (_DBG_LVL_CALLBACK,(
			"Completed Send Info:\n"
			"\tClass........:x%x\n"
			"\tMethod.......:x%X\n"
			"\tResponseBit..:x%X\n"	
			"\tAttribID.....:x%X\n"
			"\tAttribModfier;x%X\n"
			"\tTID..........:x%"PRIx64"\n",
			pSmp->common.MgmtClass,
			pSmp->common.mr.s.Method,
			pSmp->common.mr.s.R,
			pSmp->common.AttributeID,
			pSmp->common.AttributeModifier,
			pSmp->common.TransactionID
			));
		_DBG_PRINT (_DBG_LVL_CALLBACK,(
			"Completed Send Info:\n"
			"\tClass........:x%x\n"
			"\tMethod.......:x%X\n"
			"\tResponseBit..:x%X\n"	
			"\tAttribID.....:x%X\n"
			"\tHopCount.....:x%X\n"
			"\tHopPointer...:x%X\n",
			pSmp->common.MgmtClass,
			pSmp->common.mr.s.Method,
			pSmp->common.mr.s.R,
			pSmp->common.AttributeID,
			pSmp->common.u.DR.HopCount,
			pSmp->common.u.DR.HopPointer
			));
#else
		_DBG_PRINT (_DBG_LVL_CALLBACK,(
			"Completed Send Info:\n"
			"\tClass........:x%x\n"
			"\tMethod.......:x%X\n"
			"\tResponseBit..:x%X\n"	
			"\tAttribID.....:x%X\n"
			"\tAttribModfier;x%X\n"
			"\tTID..........:x%"PRIx64"\n"
			"\tHopCount.....:x%X\n"
			"\tHopPointer...:x%X\n",
			pSmp->common.MgmtClass,
			pSmp->common.mr.s.Method,
			pSmp->common.mr.s.R,
			pSmp->common.AttributeID,
			pSmp->common.AttributeModifier,
			pSmp->common.TransactionID,
			pSmp->common.u.DR.HopCount,
			pSmp->common.u.DR.HopPointer
			));
#endif	
		if ( pSmpList->SmpsIn == AtomicRead(&pSmpList->SmpsOut) )
		{

			//
			// signal caller?
			//

			if ( pSmpList->PostCallback )	// callback user
			{

				//
				// If caller does not want the status, add to bin
				//

				(pSmpList->PostCallback)(
								pSmpList->Context, 
								pSmpList->SmpBlock);
			
				//
				// Free list
				//

				MemoryDeallocate( pSmpList );
			}
			else
			{

				//
				// Do we have errors?
				//

				if ( 0 != pSmpList->SmpsInError )
				{
					if ( g_Sma->SmUserTbl )
					{
						//
						// we have an error ; call user
						//

						if ( g_Sma->SmUserTbl->DefRecv.EventCallback )
						{
							(g_Sma->SmUserTbl->DefRecv.EventCallback)(
								EVENT_SEND_ERROR,
								pSmpList->SmpBlock,
								pSmpList->Context);

							//
							// Free list
							//

							MemoryDeallocate( pSmpList );
							pSmpList = NULL;
						}
					}
				}

				//
				// No errors, pass buffer to Bin
				//

				if ( NULL != pSmpList )
				{
					status = iba_smi_pool_put( NULL, pSmpList->SmpBlock);

					//
					// Free list
					//

					MemoryDeallocate( pSmpList );
				}
			}		// callback
		}			// In == Out
	}
	else
	{
		//
		// Silently return/recycle SMP to pool
		//
		// We land here if it was an internal send or only one send
		//
		status = iba_smi_pool_put( NULL, (SMP_BLOCK *)SmpBlock);
	}
}


void
SmaAddToRecvQ(
	IN	POOL_DATA_BLOCK		*SmpBlock
	)
{

	SpinLockAcquire( &g_Sma->RQ.Lock );

	//
	// Handle common recv completion
	//

	if ( NULL != g_Sma->RQ.s.RECV.Tail )
	{
		SmpBlock->Block.Prev = g_Sma->RQ.s.RECV.Tail;
		g_Sma->RQ.s.RECV.Tail->Block.Next = SmpBlock;
		g_Sma->RQ.s.RECV.Tail = SmpBlock;
	}
	else
	{
		g_Sma->RQ.s.RECV.Head = g_Sma->RQ.s.RECV.Tail = SmpBlock;
	}

	g_Sma->RQ.Count++;

	SpinLockRelease( &g_Sma->RQ.Lock );
}


void
SmaProcessCQ(
	IN		SMA_CA_OBJ_PRIVATE	*CaObj,
	IN	OUT	RECV_BLOCK			*RecvBlock
)
{
	boolean						bRearmed		= FALSE;
	FSTATUS						status;
	POOL_DATA_BLOCK				*pSmpBlock;
	STL_SMP							*pSmp;
	IB_WORK_COMPLETION			workCompletion;
	SMA_CA_OBJ_PRIVATE			*pCaObj;
	SMA_PORT_TABLE_PRIV			*pCaPortTbl;
	uint8						PortNumber;
	SMI_ACTION					SmiAction = SMI_TO_LOCAL_SM;
	

	_DBG_ENTER_LVL(_DBG_LVL_CALLBACK, SmaProcessCQ);

	//
	// Processing completions on the CQ is done as follows:
	//	1. Repeatedly Poll till all entries are done. 
	//	2. Rearm CQ and Poll again. If there are no more completions bail out.
	//	3. If there are completions go back to step 1.
	//
	//	We follow these steps to optimize on hardware events and race 
	//	conditions.
	//
	
	while (1)
	{
		//
		// Loop and pull out all sends and receives
		//
		while ( FSUCCESS == (status = iba_poll_cq( CaObj->CqHandle, &workCompletion )) )
		{
			//
			// fill in common values
			//

			pSmpBlock = (POOL_DATA_BLOCK *)(uintn)MASK_WORKREQID(workCompletion.WorkReqId);
			
			ASSERT (pSmpBlock);

			if ( !pSmpBlock )
			{
				_DBG_ERROR (("Spurious CQ Event!!!\n"
							"\tCompleted WQE.....:(%s)\n"
							"\tSTATUS............:x%x\n"
							"\tLength............:x%x\n",
							_DBG_PTR(((WCTypeSend == workCompletion.WcType) ? "Send":"Recv")),
							workCompletion.Status,
							workCompletion.Length ));

				if ( WCTypeRecv == workCompletion.WcType )
				{
					_DBG_ERROR (("\n"
							"\tQP Number.........:x%x\n"
							"\tSourceLID.........:x%x\n",
							workCompletion.Req.RecvUD.SrcQPNumber,
							workCompletion.Req.RecvUD.SrcLID ));
				}

				_DBG_BREAK;
				break;
			}	

			pSmpBlock->Block.Status = workCompletion.Status;
			PortNumber = pSmpBlock->Block.PortNumber;
			

			_DBG_PRINT (_DBG_LVL_CALLBACK, (
				"Completed %s WQE (%p):\n"
				"\tSTATUS.......:x%X (%s)\n"
				"\tPort Number..:%d\n",
				_DBG_PTR((IS_SQ_WORKREQID(workCompletion.WorkReqId) ? "Send":"Recv")),
				_DBG_PTR(pSmpBlock),
				pSmpBlock->Block.Status, 
				_DBG_PTR(iba_fstatus_msg(pSmpBlock->Block.Status)),
				pSmpBlock->Block.PortNumber ));
		
			//
			// Is it Send?
			//
			if (IS_SQ_WORKREQID(workCompletion.WorkReqId))
			{
				//
				// Handle common send completion
				//
				SmaAddToSendQ(pSmpBlock);
			
			}
			else
			{
				boolean QpDestroy;

				//
				// Its a receive!
				//

				pSmpBlock->Block.SmpByteCount = workCompletion.Length - sizeof(IB_GRH);
				pSmpBlock->Block.RecvByteCount = workCompletion.Length;

				//
				// First update receive Q depth
				//
				pCaObj = (SMA_CA_OBJ_PRIVATE *)pSmpBlock->Block.SmaCaContext;
				pCaPortTbl = (SMA_PORT_TABLE_PRIV *)pCaObj->CaPublic.PortTbl;
				if (CaObj->CaPublic.Capabilities & CA_IS_ENHANCED_SWITCH_PORT0)
				{
					if (pSmpBlock->Block.PortNumber != 1)
					{
#ifdef  ICS_LOGGING
						_DBG_WARNING(( "RecvQ Error: Bad Port Number of %d, should be 1\n",
									   pSmpBlock->Block.PortNumber));
#else
						_DBG_WARN(( "RecvQ Error: Bad Port Number of %d, should be 1\n",
									pSmpBlock->Block.PortNumber));
#endif
						pSmpBlock->Block.PortNumber = 1;
					}
				}
				QpDestroy = ( QP_DESTROY == pCaPortTbl[pSmpBlock->Block.PortNumber-1].PortBlock->Qp0Control );
				
				AtomicIncrementVoid(&pCaPortTbl[pSmpBlock->Block.PortNumber-1].PortBlock->Qp0RecvQTop);

				if (pSmpBlock->Block.Status != FSUCCESS)
				{
					// In destroy state we expect to get back CQEs with Flush Status
					if ( ! QpDestroy )
					{
#ifdef  ICS_LOGGING
						_DBG_WARNING(( "RecvQ Error: CQE Status %d\n",
							pSmpBlock->Block.Status));
#else
						_DBG_WARN(( "RecvQ Error: CQE Status %d\n",
							pSmpBlock->Block.Status));
#endif		
					}
    
					SmiAction = SMI_DISCARD;
					goto DoAction;
				}

				//
				// Fill in receive details
				//

				//
				// Port Number and Q pair are filled-up during the iba_smi_post_recv()
				//
				pSmpBlock->Block.SLID = workCompletion.Req.RecvUD.SrcLID;
				if (pSmpBlock->Block.SLID == LID_PERMISSIVE)
					pSmpBlock->Block.SLID = STL_LID_PERMISSIVE;
				pSmpBlock->Block.PathBits = workCompletion.Req.RecvUD.DestPathBits;
				pSmpBlock->Block.ServiceLevel = workCompletion.Req.RecvUD.ServiceLevel;
				// BUGBUG - need to get GRH header out of workCompletion

				// IB does not have Static rate in UD LRH
				// so we can't be sure what rate of remote port is
				// we use a constant value for SMI QPs
				pSmpBlock->Block.StaticRate = IB_STATIC_RATE_SMI;
				_DBG_PRINT (_DBG_LVL_CALLBACK, (
					"Inbound PathBits: %d\n",
					pSmpBlock->Block.PathBits));
				
				pSmp = (STL_SMP*)pSmpBlock->Block.Smp;
				//
				// Network Byte order block, make Host Byte Order
				//
				BSWAP_STL_SMP_HEADER(pSmp);



				_DBG_PRINT (_DBG_LVL_CALLBACK, (
					"Receive Info:\n"
					"\tClass........:x%x\n"
					"\tAttribID.....:x%X\n"
					"\tAttribModfier:x%X\n"
					"\tMethod.......:x%X\n"
					"\tResponseBit..:x%X\n"
					"\tHopCount.....:x%X\n",
					pSmp->common.MgmtClass,
					pSmp->common.AttributeID,
					pSmp->common.AttributeModifier,
					pSmp->common.mr.s.Method,
					pSmp->common.mr.s.R,
					pSmp->common.u.DR.HopCount
					));
				_DBG_PRINT (_DBG_LVL_CALLBACK, (
					"Receive Info (cont):\n"
					"\tHopPointer...:x%X\n"
					"\tTID..........:x%"PRIx64"\n"
					"\tSLID.........:x%X\n",
					pSmp->common.u.DR.HopPointer,
					pSmp->common.TransactionID,
					pSmpBlock->Block.SLID
					));

				// Trap with SLID 0 is a special case
				// Firmware has provided a trap to us on QP0
				// which we need to send out
				// use SmaProcessSmaSmpRcvd to allow the driver to complete
				// setup of the mad.  If driver does not want to send it
				// FNOT_DONE is returned
				if ( MMTHD_TRAP == pSmp->common.mr.s.Method
					  && 0 == pSmpBlock->Block.SLID)
				{
					// Let Driver fixup mad if needed
					if (FALSE == SmaProcessSmaSmpRcvd(pSmpBlock))
					{
						SmiAction = SMI_DISCARD;
					} else {
						// output on wire, assume its LID routed and valid
						// so no need to do SmaProcessSmiSmp DR algorithms
						// if its to our own LID (local SM), it will reappear
						// on our recv queue and get to SM that way
						SmiAction = SMI_SEND;
					}
				} else {
					// Validate incoming Smp per common Header
					// and Directed Route Checks
					// LID routed SMPs are destined to local SMA/SM
					SmiAction = SmaProcessSmiSmp(pCaObj, pSmpBlock, TRUE);

					if (SMI_TO_LOCAL == SmiAction
						|| SMI_LID_ROUTED == SmiAction)
					{
						SmiAction = SmaProcessLocalSmp(pCaObj, pSmpBlock);
						if (SMI_LID_ROUTED == SmiAction)
						{
							// LID routed responses sent back to requestor
							SmiAction = SMI_SEND;
						}
					}
				}

DoAction:
				if (SmiAction == SMI_SEND)
				{
					// output on wire, DR processing already completed
					pSmpBlock->Base = NULL;
					pSmpBlock->CQTrigger = FALSE;// Do not ask for interrupt. 
												// Flush SMP in next cycle

					if (FSUCCESS != SmaPostSmp( pCaObj, pSmpBlock ))
					{
						// unable to send
						SmiAction = SMI_DISCARD;
					}
				}

				if ( SMI_TO_LOCAL_SM == SmiAction )
				{
					// Add to recv Q for SM, else (no SM) return to pool
					if (( NULL != g_Sma->SmUserTbl )
						&& ( NULL != g_Sma->SmUserTbl->DefRecv.RcvCallback ) )
					{
						_DBG_PRINT (_DBG_LVL_CALLBACK,(
							"Adding SMP to RQ\n" ));
											
						if ( NULL == RecvBlock->Head )
						{
							RecvBlock->Head = RecvBlock->Tail = pSmpBlock;
						}
						else
						{
							RecvBlock->Tail->Block.Next = pSmpBlock;
							pSmpBlock->Block.Prev		= RecvBlock->Tail;
							RecvBlock->Tail				= pSmpBlock;
						}

						RecvBlock->Count++;		// keep count for quick q insertion
					}	
					else
					{
						SmiAction = SMI_DISCARD;
					}
				}

				if (SMI_DISCARD == SmiAction)
				{
					status = iba_smi_pool_put( NULL, (SMP_BLOCK *)pSmpBlock);
				}

				//
				// Post a SMP to recvQ to recv new msgs
				//
				// only post if not in process of destroying QP
				if ( workCompletion.Status != WRStatusWRFlushed && ! QpDestroy )
				{
					status = iba_smi_post_recv( NULL, pCaObj, PortNumber, 1);
					if ( FSUCCESS != status )
					{
						_DBG_ERROR (( "Could not post to RecvQ!!!\n" ));
					}
				}
			}			// recv

			SmiAction = SMI_TO_LOCAL_SM;

		}				// iba_poll_cq while loop


		if ( FNOT_DONE != status )
		{
			_DBG_ERROR (("Got %s Status!!!\n", _DBG_PTR(FSTATUS_MSG(status)) ));
		}
		
		//
		// Rearm CQ for race conditions
		//
		if ( FALSE == bRearmed )
		{
			status = iba_rearm_cq( CaObj->CqHandle, CQEventSelNextWC);
			bRearmed = TRUE;
		}
		else
		{
			break;
		}
	}			// while 1 for ReArm

    _DBG_LEAVE_LVL(_DBG_LVL_CALLBACK);

}

// When CaContext != NULL, we are in a thread context, as such we can preempt
// note that SmaProcessCQ can call GetSetMad in the VPD which might prempt
void 
SmaCompletionCallback(
	IN	void *CaContext
	)
{
	SMA_CA_OBJ_PRIVATE	*pCaObj;
	RECV_BLOCK			recvBlock;
	
	_DBG_ENTER_LVL(_DBG_LVL_CALLBACK, SmaCompletionCallback);

	pCaObj = (SMA_CA_OBJ_PRIVATE*)CaContext;

	// Init Recv context
	recvBlock.Head = recvBlock.Tail = NULL;
	recvBlock.Count = 0;

	//lookup request and fill in appropriate post set

	if ( NULL != pCaObj )
		SmaProcessCQ(pCaObj, &recvBlock);

	// We optimize on Lock usage by taking from RQ
	// then looking up client definitions

	SpinLockAcquire( &g_Sma->RQ.Lock );

	// Move contents of RQ to recvBlock
	if ( 0 != g_Sma->RQ.Count ) 
	{
		if (recvBlock.Count)
		{
			// add recvBlock contents at end of RQ
			g_Sma->RQ.s.RECV.Tail->Block.Next = recvBlock.Head;
			recvBlock.Head->Block.Prev = g_Sma->RQ.s.RECV.Tail;
			g_Sma->RQ.s.RECV.Tail = recvBlock.Tail;
		}

		// recvBlock gets all of combined RQ
		recvBlock.Head = g_Sma->RQ.s.RECV.Head;
		recvBlock.Tail = g_Sma->RQ.s.RECV.Tail;
		
		g_Sma->RQ.s.RECV.Head = g_Sma->RQ.s.RECV.Tail = NULL;

		recvBlock.Count += g_Sma->RQ.Count;
		g_Sma->RQ.Count = 0;
	}

	SpinLockRelease( &g_Sma->RQ.Lock );

	// Look up if we have receives and client defined
	// Else recycle buffers
	//

	// Process recvs, Distribute through User Callbacks

	if ( 0 != recvBlock.Count )
	{
		if ( ( NULL != g_Sma->SmUserTbl ) && \
			 ( NULL != g_Sma->SmUserTbl->DefRecv.RcvCallback ) )
		{
			_DBG_PRINT(_DBG_LVL_CALLBACK,
				("Received[x%x] for Callback[x%p] Context[x%p]\n",
				 recvBlock.Count,
				 _DBG_PTR(g_Sma->SmUserTbl->DefRecv.RcvCallback),
				 _DBG_PTR(g_Sma->SmUserTbl->DefRecv.Context)
				 ));
		
			// call user callback
			( g_Sma->SmUserTbl->DefRecv.RcvCallback )(
				g_Sma->SmUserTbl->DefRecv.Context,
				(SMP_BLOCK *)recvBlock.Head);
		} else {
			// recycle buffers
			SmaReturnToBin(recvBlock.Head);
		}
	}

    _DBG_LEAVE_LVL(_DBG_LVL_CALLBACK);
}




//
// The common completion event handler provided to the verbs consumer
//

void 
SmaGsaCompletionCallback(
	IN	void *CaContext, 
	IN	void *CqContext
	)
{
	SMA_CA_OBJ_PRIVATE	*pCaObj = (SMA_CA_OBJ_PRIVATE*)CaContext;
	
	_DBG_ENTER_LVL(_DBG_LVL_CALLBACK, SmaGsaCompletionCallback);

	if ( (void *)QPTypeSMI == CqContext )
	{
		EventThreadSignal(&pCaObj->SmaEventThread);
	} else if ( (void *)QPTypeGSI == CqContext ) {
		EventThreadSignal(&pCaObj->GsaEventThread);
	}
    _DBG_LEAVE_LVL(_DBG_LVL_CALLBACK);
}



//
// The asynchronous error handler provided to the verbs consumer
//

void
SmaAsyncEventCallback(
	IN	void				*CaContext, 
	IN IB_EVENT_RECORD		*EventRecord
	)
{
	SMA_CA_OBJ_PRIVATE		*pCaObj;
	SMA_PORT_TABLE_PRIV		*pPortTbl;

	_DBG_ENTER_LVL(_DBG_LVL_CALLBACK, SmaAsyncEventCallback);


	_DBG_PRINT(_DBG_LVL_CALLBACK,(\
		"Received Async Event:\n"
		"\tUserCaContext..: x%p\n"
		"\tContext........: x%p\n"
		"\tEventType......: x%x\n"
		"\tEventCode......: x%x\n",
		_DBG_PTR(CaContext),
		_DBG_PTR(EventRecord->Context),
		EventRecord->EventType,
		EventRecord->EventCode));


	pCaObj = (SMA_CA_OBJ_PRIVATE*)CaContext;
	pPortTbl = (SMA_PORT_TABLE_PRIV *)pCaObj->CaPublic.PortTbl;

	switch ( EventRecord->EventType )
	{
	default:
		break;
	
	case	AsyncEventQP:
		{
			_DBG_PRINT(_DBG_LVL_CALLBACK,("AsyncEventQP\n"));
			break;
		}

	case	AsyncEventSendQ:
		{
			_DBG_PRINT(_DBG_LVL_CALLBACK,("AsyncEventSendQ\n"));
			break;
		}

	case	AsyncEventRecvQ:
		{
			_DBG_PRINT(_DBG_LVL_CALLBACK,("AsyncEventRecvQ\n"));
			break;
		}

	case	AsyncEventCQ:
		{
			_DBG_PRINT(_DBG_LVL_CALLBACK,("AsyncEventCQ\n"));
			break;
		}

	case	AsyncEventCA:
		{
			_DBG_PRINT(_DBG_LVL_CALLBACK,("AsyncEventCA\n"));
			break;
		}

	case	AsyncEventPort:
		{
			uint8					portNo;
			IB_PORT_STATE			portState, oldPortState;
			SMA_PORT_BLOCK_PRIV		*pPortBlock;

			_DBG_PRINT(_DBG_LVL_CALLBACK,("AsyncEventPort\n"));

			//
			// context field = 1 based port number 
			// event type = port event
			// event code = port state
			//

			portNo = (uint8)((uintn)EventRecord->Context);
			portState = (IB_PORT_STATE)EventRecord->EventCode;

			pPortBlock = &pPortTbl->PortBlock[portNo-1];
			oldPortState = pPortBlock->Public.State;
			pPortBlock->Public.State = portState;

			/* ignore port state Async events while shutting down CA */
			if ( pPortBlock->Qp0Control != QP_DESTROY
				&& oldPortState != portState )
			{
				if ( IB_PORT_DOWN == portState )
				{
					IbtNotifyGroup(
						pPortBlock->Public.GUID,
						IB_NOTIFY_PORT_DOWN );	
				}

				if ( IB_PORT_ACTIVE == portState )
				{
					IbtNotifyGroup(
						pPortBlock->Public.GUID,
						IB_NOTIFY_LID_EVENT );	
				}
			}

			break;
		}

	case	AsyncEventPathMigrated:
		{
			_DBG_PRINT(_DBG_LVL_CALLBACK,("AsyncEventPathMigrated\n"));
			break;
		}
	}

    _DBG_LEAVE_LVL(_DBG_LVL_CALLBACK);
}

