/*!
  @file           ven73.c
  @author         JoergM
  @brief          Kernel RunTime: Queue Handling
  @see            

\if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2001-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end



\endif
*/

#define MOD__ "ven73.c:"

/* exported functions */

#include "gen00.h"
#include "heo00.h"
#include "geo50_0.h"
#include "gen500.h"
#include "gen73.h"
#include "RunTime/System/RTESys_MemoryBarrier.h"
#include "RunTime/System/RTESys_AtomicOperation.h"

/*
 *============================================================================
 */

#undef MF__
#define MF__ MOD__"e73_dev_ioc_queue_init"
void e73_dev_ioc_queue_init ( 
struct   IOC_QUEUE             * ioc ,
tsp00_Int4                         number )
{
    int                             i ;
    struct  IOC_QUEUE             * ioc1 ;
    
    
    DBG1 (( MF__,"called,    ioc     0x%lx \n", ioc  ));
    DBG1 (( MF__,"           number  %ld   \n", number ));
    
    FILL ( ioc , 0 , sizeof(struct IOC_QUEUE) * number );
    
    /*
    * create a circular list !!
    */
    for ( ioc1 = ioc , i = 0 ; i < number ; i ++ )
    {
        ioc1->next     = ioc1 + 1;
        ioc1->previous = ioc1 - 1;
        ioc1 ++ ;
    }
    
    ioc1 -- ;
    ioc->previous = ioc1 ;
    ioc1->next    = ioc ;
    
    DBGOUT;
}

/*
 * ===========================================================================
 */
#undef MF__
#define MF__ MOD__"e73_xxx_queue_init"
void e73_xxx_queue_init ( 
struct  XXX_QUEUE_HEAD        * xxx ,
int     nr_of_elem )
{
    int                             i ;
    struct  XXX_QUEUE             * xxx1 ;
    
    DBG1 (( MF__,"called,    xxx 0x%lx \n", xxx  ));
    FILL ( xxx->element , 0 , sizeof(struct XXX_QUEUE) * nr_of_elem );
    /*
    * create a circular list !!
    */
    
    for ( xxx1 = & xxx->element [ 0 ] , i = 0 ; i < nr_of_elem ; i ++ )
    {
        xxx1->next = xxx1 + 1 ;
        xxx1 ++ ;
    }
    
    xxx1 -- ;
    xxx1->next      = & xxx->element [ 0 ];
    xxx->xx_work    = & xxx->element [ 0 ];
    xxx->xx_request = & xxx->element [ 0 ];
    xxx->num_overflows = 0;
    INIT_LOCK (xxx->exclusive) ;
    DBGOUT;
}

/*
 *============================================================================
 */
#undef MF__
#define MF__ MOD__"e73_ioc_queue_init"
void e73_ioc_queue_init ( 
struct  IOC_QUEUE_HEAD        * head ,
struct  IOC_QUEUE             * ioc ,
tsp00_Int4                        number )
{
#if defined(OLD_SPINLOCK_IOC_QUEUE) || !defined(RTESYS_HAS_NATIVE_ATOMIC_OPERATION)
    int                             i ;
    struct  IOC_QUEUE             * ioc1 ;
    
    DBG1 (( MF__,"called, head    0x%lx \n", head ));
    DBG1 (( MF__,"        ioc     0x%lx \n", ioc  ));
    DBG1 (( MF__,"        number  %d    \n", number ));
    
    ioc1 = ioc ;
    for ( i = 0 ; i < number ; i ++ )
    {
        ioc1->request = NULL ;
        ioc1->next    = ioc1 + 1 ;
        ioc1->previous= ioc1 - 1;
        ioc1 ++ ;
    }
    
    ioc1 -- ;
    ioc->previous   = ioc1 ;
    ioc1->next      = ioc ;
    
    head->first     = ioc ;
    head->last      = ioc ;
    INIT_LOCK (head->exclusive) ;
#ifdef DEBUG_RTE
    ioc1 = ioc + ( number - 1 );
    DBG1 (( MF__,"Last element %d ioc_last 0x%lx previous 0x%lx next 0x%lx\n", number, ioc1, ioc1->previous, ioc1->next  ));
#endif /* DEBUG_RTE */    
    DBGOUT;
#else
    head->last      = 0;
#endif
}

/*
 *============================================================================
 */

#undef MF__
#define MF__ MOD__"e73_ior_queue_init"
void e73_ior_queue_init ( 
struct  IOR_QUEUE_HEAD        * head ,
struct  IOR_QUEUE             * ior ,
tsp00_Int4                        number )
{
    int                             i ;
    struct  IOR_QUEUE             * ior1 ;
    
    DBG1 (( MF__,"called, head    0x%lx \n", head ));
    DBG1 (( MF__,"        ior     0x%lx \n", ior  ));
    DBG1 (( MF__,"        number  %d    \n", number ));
    
    ior1 = ior ;
    for ( i = 0 ; i < number ; i ++ )
    {
        ior1->request = NULL ;
        ior1->next    = ior1 + 1 ;
        ior1->previous= ior1 - 1;
        ior1 ++ ;
    }
    
    ior1 -- ;
    ior->previous   = ior1 ;
    ior1->next      = ior ;
    
    head->first     = ior ;
    head->last      = ior ;
    
    DBGOUT;
}

/*
 *============================================================================
 */
#undef MF__
#define MF__ MOD__"e73_ioc_enqu"
int e73_ioc_enqu ( 
struct  IOC_QUEUE_HEAD        * head ,
struct  DOUBLY_LINKED         * new_entry )
{
#if defined(OLD_SPINLOCK_IOC_QUEUE) || !defined(RTESYS_HAS_NATIVE_ATOMIC_OPERATION)
    int     enqu_ok ;
   
    DBG1 (( MF__,"head                 0x%lx \n", head ));
	DBG1 (( MF__,"head->first          0x%lx \n", head->first ));
	DBG1 (( MF__,"head->first->request 0x%lx \n", head->first->request ));
	DBG1 (( MF__,"new_entry            0x%lx \n", new_entry ));

	WAIT_UNTIL_ALONE ( head->exclusive );
	/*
	 * we are alone now
	 */
	if ( (enqu_ok = head->first->request == 0) ) 
	{
	    head->first->request = new_entry ;
		head->first          = head->first->next ;
	}
	CLEARLOCK ( head->exclusive );

	DBGOUT;
	return ( enqu_ok ) ;
#else
    void **oldValue;
    /* enqueue i/o completion */
    new_entry->forward = 0;

    /* ---- loop until we have successfully replaced head of queue entry */
    do
    {
        new_entry->backward = head->last;
        RTESys_WriteMemoryBarrier();
    }
    while ( !RTESys_CmpxchgPointer( (void **)&head->last,
                                     new_entry->backward,
                                     new_entry,
                                    (void **)&oldValue ) );
	return ( true ) ;
#endif
}

/*
 *============================================================================
 */

#undef MF__
#define MF__ MOD__"e73_dl_init"
void e73_dl_init ( 
struct  DOUBLE_Q_HEAD         * head ,
char                          * name )
{
	DBGIN;
	head->first     = 0 ;
	head->last      = 0 ;
	INIT_LOCK (head->dq_exclusive) ;
	(void) strncpy ( head->name , name , sizeof(head->name)-1 );
	DBGOUT;
}

/*
 *============================================================================
 */
#undef MF__
#define MF__ MOD__"e73_dl_enqu"
void e73_dl_enqu ( 
struct  DOUBLE_Q_HEAD         * head ,
struct  DOUBLY_LINKED         * new_entry )
{
struct  DOUBLY_LINKED         * lquu ;

	DBG1 (( MF__,"called, head   '%s' \n", head->name ));
	DBG1 (( MF__,"        first  0x%lx \n", head->first ));

/* JH,HKL
*/
/* http://pts:1080/webpts?wptsdetail=yes&ErrorType=0&ErrorID=1127071 */
    WAIT_UNTIL_ALONE ( head->dq_exclusive );
/*
 * we are alone now
 */
/*
 *  The following conditional prevents this funtion from being used
 *  for other purposes than FREELIST.
 */
	if ( (new_entry < KGS->first_free) || (new_entry > KGS->last_free) )
	{
		MSGALL (( IERR_QUE_NON_FREEL_ELEMENT )) ; 
		MSGALL (( IERR_QUE_FREEL_FIRST_LAST, 
		(long) new_entry , (long) KGS->first_free , (long) KGS->last_free ))
		vabort ( WRITE_CORE );
	}
	for ( lquu = head->last ; lquu ; lquu = lquu->backward )
	{
		if ( lquu == new_entry )
		{
			MSGALL (( IERR_QUE_FREEING_TWICE )) ;
			MSGALL (( IERR_QUE_FREEL_FIRST_LAST, 
			(long) new_entry , (long) KGS->first_free , (long) KGS->last_free ));
			vabort ( WRITE_CORE );
		}
	}

	new_entry->forward   = head->first ;
	new_entry->backward  = 0 ;
	if ( head->first )
    {
		new_entry->forward->backward = new_entry ;
    }
	else
    {
		head->last                   = new_entry ;
    }
	head->first          = new_entry ;
	CLEARLOCK ( head->dq_exclusive );

	DBGOUT;
}

/*
 *============================================================================
 */
#undef MF__
#define MF__ MOD__"e73_dl_dequ"
struct  DOUBLY_LINKED   * e73_dl_dequ (
struct  DOUBLE_Q_HEAD         * head )
{
struct  DOUBLY_LINKED         * lquu ;

	DBG1 (( MF__,"called, head   '%s' \n", head->name ));
	DBG1 (( MF__,"        first  0x%lx \n", head->first ));
	DBG1 (( MF__,"        last   0x%lx \n", head->last ));

	WAIT_UNTIL_ALONE ( head->dq_exclusive );
/*
 * we are alone now
 */
	if ( ! head->first )
    {
		/*
		 * Queue is empty
		 */
		lquu = NULL ;
		DBG1 (( MF__,"queue is empty \n" ));
    }
	else
    {
		lquu = head->last ;
		if ( lquu->backward )
		{
			head->last              = lquu->backward ;
			lquu->backward->forward = NULL ;
			lquu->backward          = NULL ;
		}
		else
		{
			head->first = NULL ;
			head->last  = NULL ;
		}
    }

	CLEARLOCK ( head->dq_exclusive );

	DBG1 (( MF__,"returning 0x%lx \n", lquu ));
	DBG1 (( MF__,"first     0x%lx \n", head->first ));
	return ( lquu );
}
