/////////////////////////////////////////////////////////////////////////////////////
//                                                                      
//                  I N T E L   P R O P R I E T A R Y                   
//                                                                      
//     COPYRIGHT (c)  2001 BY  INTEL  CORPORATION.  ALL RIGHTS          
//     RESERVED.   NO  PART  OF THIS PROGRAM  OR  PUBLICATION  MAY      
//     BE  REPRODUCED,   TRANSMITTED,   TRANSCRIBED,   STORED  IN  A    
//     RETRIEVAL SYSTEM, OR TRANSLATED INTO ANY LANGUAGE OR COMPUTER    
//     LANGUAGE IN ANY FORM OR BY ANY MEANS, ELECTRONIC, MECHANICAL,    
//     MAGNETIC,  OPTICAL,  CHEMICAL, MANUAL, OR OTHERWISE,  WITHOUT    
//     THE PRIOR WRITTEN PERMISSION OF :                                
//                                                                      
//                        INTEL  CORPORATION                            
//                                                                     
//                     2200 MISSION COLLEGE BLVD                        
//                                                                      
//               SANTA  CLARA,  CALIFORNIA  95052-8119                  
//                                                                      
/////////////////////////////////////////////////////////////////////////////////////
//
// 		File Name: scheduler_qm.c
// 
// 		Purpose:  QM message handling for Egress Packet Scheduler
//
/////////////////////////////////////////////////////////////////////////////////////
//
// 		History:
//
// 		Date			Comment										By
//		---------------------------------------------------------------------------
//
//		09/17/02		Created										Uday Naik
//
/////////////////////////////////////////////////////////////////////////////////////

#include "scheduler_packet.h"

/////////////////////////////////////////////////////////////////////////////////////
//
// SchedulerQmHandleEnqTransition()
//
// Description:
// 	
//		Handle an enq transition message from the QM
//
// Inputs:  
//
//		message:				QM Message
//
//
/////////////////////////////////////////////////////////////////////////////////////


INLINE void SchedulerQmHandleEnqTransition (qm_message_t message)
{

	__declspec(local_mem shared aligned(32)) port_t  *pPort;
													

	// Get a pointer to the port structure 

	pPort = &globalPorts[message.portNumber];
	
	// Set the bit for this queue in the queue empty vector for this port

	pPort->portQueueEmptyVector |= (1 << message.queueNumber);


	// Also if the bit for this group was 0 in the parent vector, we need to set it.
	// Rather than check for the bit being 0, we will simply set the bit	
	
	globalPortEmptyVector |= (1 << message.portNumber);

}

/////////////////////////////////////////////////////////////////////////////////////
//
// SchedulerQmHandleDeqTransition()
//
// Description:
// 	
//		Handle a Dequeue transition message from the QM
//
// Inputs:  
//
//		message:					QM Message
//		pPort						pointer in local memory to port data structure
//
/////////////////////////////////////////////////////////////////////////////////////

INLINE void SchedulerQmHandleDeqTransition 
(
	qm_message_t 	message, 
	__declspec(local_mem shared aligned(32))	port_t 			*pPort
)
{

	// clear the bit in the empty vector for this queue 

	if ((pPort->portQueueEmptyVector &= ~(1 << message.queueNumber)) == 0)
	{
		
		// If the vector goes to 0 then clear the bit in the parent 

		globalPortEmptyVector &= ~(1 << message.portNumber);
	}

}


/////////////////////////////////////////////////////////////////////////////////////
//
// SchedulerQmAdjustCredit[]
//
// Description:
// 	
//		Adjusts the DRR credit for a queue. If queue is negative it marks it as non 
//		schedulable and gives it another round of credit. If all queues are not 
//		schedulable, the DRR round is over and all queues are again schedulable
//
// Outputs: 
//
//								None
// Inputs:  
//
//		in_packet_length:			length of the current packet 
//		in_queue_bitmask			1 << queue_number 
//		in_q_mask					all 1s stored in a register
//
// Constants
//
//		EXIT_LABEL 					Label to branch to 
//
// Size: 
//
//		11 instruction 
//
/////////////////////////////////////////////////////////////////////////////////////

INLINE void SchedulerQmAdjustCredit
( 
	qm_message_t 	message,
	__declspec(local_mem shared aligned(32))	port_t 			*pPort,
	__declspec(local_mem shared aligned(8))		queue_t 		*pQueue
)
{

	// decrement the credit 

	pQueue->queueCurrentCredit--;

	// if credit is not positive, we need to mark the queue as non schedulable and 
	// give it another round of credit 
	
	if (pQueue->queueCurrentCredit <= 0)
	{

		// mark queue as non schedulable 

		pPort->portScheduleVector &= ~(1 << message.queueNumber);

		// Give credit increment to queue

		pQueue->queueCurrentCredit += pQueue->queueCreditIncrement;

		// check if any queue is schedulable 

		if ((pPort->portScheduleVector & pPort->portQueueEmptyVector) == 0)
		{
			
			// Reset the schedule vector to all 1

			pPort->portScheduleVector = ~0;

		}

	}
	
}  
		
/////////////////////////////////////////////////////////////////////////////////////
//
// SchedulerQmMessageHandler()
//
// Description:
// 	
//		Thread that handles QM messages
// 
//
/////////////////////////////////////////////////////////////////////////////////////

INLINE SchedulerQmMessageHandler(void)
{

	qm_message_t  	message;		
	__declspec(local_mem shared aligned(32))	port_t 			*pPort;
	__declspec(local_mem shared aligned(8))		queue_t 		*pQueue;	
	
	// Loop handling messages from Queue Manager 

	while (1)
	{

		// loop until there is a message on the NN ring

		do 
		{

			// swap out. The first time the thread runs it will swap out immediately

			ctx_swap();

		}  while (inp_state_test(inp_state_nn_empty));

		// Read the enqueue message

		message.value = nn_ring_dequeue_incr();

		// Check if it is an enqueue transition.  

		if (message.enqueueDequeueTransition)
		{

			// Handle the enqueue transition 

			SchedulerQmHandleEnqTransition(message);

		}

		// Read the dequeue message 

		message.value = nn_ring_dequeue_incr();

		// Check if it is a valid message - if not go to back to top of loop

		if (message.valid == 0)
			continue;

#ifndef WORKAROUND

		// Get a pointer to the current queue pointed to by the message 

		pQueue = &globalQueues[message.queueId];

#else

		pQueue = &globalQueues[message.queueNumber + (message.portNumber << 4)];

#endif

		// Get a pointer to the current port pointed to by the message 

		pPort = &globalPorts[message.portNumber];

		// Check for invalid dequeue 

		if (message.invalidDequeue == 0)
		{
			
			// check for dequeue transition 

			if (message.enqueueDequeueTransition)
			{

				// Now handle the dequeue transition 

				SchedulerQmHandleDeqTransition(message, pPort);

			}

			// Adjust the DRR credit 

			SchedulerQmAdjustCredit(message, pPort, pQueue);


		}
		else
		{
			
			// decrement the packets scheduled 

			pPort->portPacketsScheduled--;

		} // end if message.invalidDequeue == 0


	}	// end while 

}
	
/////////////////////////////////////////////////////////////////////////////////////
	
